From 3acc87a620034445ce7ee7f0cad052f08f690619 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Sat, 10 Jan 2026 15:44:02 +0100 Subject: [PATCH] [SSE] Add backend for SSE subscriptions (#17022) - moved a few gql types to twenty shared to re-use in server - added a new endpoint, onEventSubscription that expect a streamId to create a connection - two new endpoints to store queries in Redis - updated the existing batch channel to directly use object record type --- .../src/generated-metadata/graphql.ts | 58 ++++++++++ .../twenty-front/src/generated/graphql.ts | 58 ++++++++++ .../FindOneCalendarEventOperationSignature.ts | 2 +- .../modules/activities/hooks/useActivities.ts | 2 +- .../useActivityTargetsForTargetableObjects.ts | 2 +- .../activities/notes/hooks/useNotes.ts | 2 +- .../states/currentNotesQueryVariablesState.ts | 2 +- .../FindManyTimelineActivitiesOrderBy.ts | 2 +- .../utils/sortCachedObjectEdges.ts | 7 +- .../types/CachedObjectRecordQueryVariables.ts | 2 +- .../hooks/useGetObjectOrderByField.ts | 6 +- .../utils/generateCreateOneRecordMutation.ts | 6 +- .../utils/generateUpdateOneRecordMutation.ts | 6 +- .../utils/getObjectOrderByField.ts | 6 +- .../utils/getOrderByForFieldMetadataType.ts | 6 +- .../utils/shouldFieldBeQueried.ts | 2 +- .../useReadFindManyRecordsQueryInCache.ts | 2 +- .../useUpsertFindManyRecordsQueryInCache.ts | 2 +- .../utils/getRecordConnectionFromRecords.ts | 2 +- .../RecordGqlOperationGqlRecordFields.ts | 3 - .../RecordGqlOperationGroupByVariables.ts | 6 +- .../types/RecordGqlOperationSignature.ts | 8 -- .../RecordGqlOperationSignatureFactory.ts | 2 +- .../types/RecordGqlOperationVariables.ts | 13 --- .../hooks/useCreateManyRecords.ts | 2 +- .../hooks/useCreateManyRecordsMutation.ts | 2 +- .../object-record/hooks/useCreateOneRecord.ts | 2 +- .../hooks/useCreateOneRecordMutation.ts | 2 +- .../useFetchMoreRecordsWithPagination.ts | 8 +- .../object-record/hooks/useFindManyRecords.ts | 8 +- .../hooks/useFindManyRecordsQuery.ts | 6 +- .../object-record/hooks/useFindOneRecord.ts | 2 +- .../hooks/useFindOneRecordQuery.ts | 2 +- .../hooks/useGroupByRecordsQuery.ts | 2 +- .../useLazyFetchMoreRecordsWithPagination.ts | 8 +- .../hooks/useLazyFindOneRecord.ts | 2 +- .../hooks/useMergeManyRecords.ts | 2 +- .../hooks/useMergeManyRecordsMutation.ts | 2 +- .../hooks/useUpdateOneRecordMutation.ts | 2 +- .../useCombinedFindManyRecords.test.tsx | 2 +- .../hooks/useCombinedFindManyRecords.ts | 2 +- .../hooks/useCombinedGetTotalCount.ts | 2 +- ...useGenerateCombinedFindManyRecordsQuery.ts | 2 +- .../usePerformCombinedFindManyRecords.ts | 2 +- ...binedFindManyRecordsQueryVariables.test.ts | 2 +- ...teCombinedFindManyRecordsQueryVariables.ts | 2 +- .../__tests__/turnSortsIntoOrderBy.test.ts | 2 +- .../utils/turnSortsIntoOrderBy.ts | 6 +- .../generateGroupsRecordsGroupByQuery.ts | 6 +- .../hooks/useRecordCalendarGroupByRecords.ts | 6 +- .../select/hooks/useRecordsForSelect.ts | 2 +- .../types/UseFindManyRecordsParams.ts | 6 +- .../utils/generateFindManyRecordsQuery.ts | 9 +- .../utils/generateGroupByRecordsQuery.ts | 6 +- .../object-record/utils/getQueryIdentifier.ts | 2 +- ...sObjectLevelObjectFieldPermissionTable.tsx | 2 +- .../hooks/__tests__/useSortedArray.test.tsx | 2 +- .../ui/layout/table/types/TableSortValue.ts | 2 +- .../src/utils/turnOrderByIntoSort.ts | 2 +- .../types/cache-storage-namespace.enum.ts | 1 + .../dtos/add-query-subscription.input.ts | 16 +++ .../dtos/event-subscription.dto.ts | 21 ++++ .../object-record-event-properties.dto.ts | 18 +++ .../dtos/object-record-event.dto.ts | 21 ++++ .../dtos/remove-query-subscription.input.ts | 10 ++ .../enums/subscription-channel.enum.ts | 2 +- .../subscriptions/event-stream.service.ts | 109 ++++++++++++++++++ .../subscriptions/subscriptions.module.ts | 14 ++- .../workspace-event-emitter.resolver.ts | 102 +++++++++++++++- .../workspace-event-emitter.service.ts | 4 +- .../src}/types/OrderBy.ts | 0 .../src/types/QueryCursorDirection.ts | 1 + .../src}/types/RecordGqlFieldsDeprecated.ts | 0 .../RecordGqlOperationGqlRecordFields.ts | 3 + .../src}/types/RecordGqlOperationOrderBy.ts | 2 +- .../src/types/RecordGqlOperationSignature.ts | 8 ++ .../src/types/RecordGqlOperationVariables.ts | 13 +++ packages/twenty-shared/src/types/index.ts | 7 ++ 78 files changed, 567 insertions(+), 111 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGqlRecordFields.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignature.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationVariables.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/dtos/add-query-subscription.input.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/dtos/event-subscription.dto.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/dtos/object-record-event-properties.dto.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/dtos/object-record-event.dto.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/dtos/remove-query-subscription.input.ts create mode 100644 packages/twenty-server/src/engine/subscriptions/event-stream.service.ts rename packages/{twenty-front/src/modules => twenty-shared/src}/types/OrderBy.ts (100%) create mode 100644 packages/twenty-shared/src/types/QueryCursorDirection.ts rename packages/{twenty-front/src/modules/object-record/graphql => twenty-shared/src}/types/RecordGqlFieldsDeprecated.ts (100%) create mode 100644 packages/twenty-shared/src/types/RecordGqlOperationGqlRecordFields.ts rename packages/{twenty-front/src/modules/object-record/graphql => twenty-shared/src}/types/RecordGqlOperationOrderBy.ts (91%) create mode 100644 packages/twenty-shared/src/types/RecordGqlOperationSignature.ts create mode 100644 packages/twenty-shared/src/types/RecordGqlOperationVariables.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 95e42c58250..2c2b53d2b89 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -25,6 +25,12 @@ export type ActivateWorkspaceInput = { displayName?: InputMaybe; }; +export type AddQuerySubscriptionInput = { + eventStreamId: Scalars['String']; + operationSignature: Scalars['JSON']; + queryId: Scalars['String']; +}; + export type AdminPanelHealthServiceData = { __typename?: 'AdminPanelHealthServiceData'; description: Scalars['String']; @@ -1311,6 +1317,18 @@ export enum EmailingDomainStatus { VERIFIED = 'VERIFIED' } +export type EventSubscription = { + __typename?: 'EventSubscription'; + eventStreamId: Scalars['String']; + eventWithQueryIdsList: Array; +}; + +export type EventWithQueryIds = { + __typename?: 'EventWithQueryIds'; + event: ObjectRecordEvent; + queryIds: Array; +}; + export type ExecuteServerlessFunctionInput = { /** Id of the serverless function to execute */ id: Scalars['UUID']; @@ -1799,6 +1817,7 @@ export type Mutation = { activateSkill: Skill; activateWorkflowVersion: Scalars['Boolean']; activateWorkspace: Workspace; + addQueryToEventStream: Scalars['Boolean']; assignRoleToAgent: Scalars['Boolean']; assignRoleToApiKey: Scalars['Boolean']; authorizeApp: AuthorizeAppOutput; @@ -1914,6 +1933,7 @@ export type Mutation = { initiateOTPProvisioning: InitiateTwoFactorAuthenticationProvisioningOutput; initiateOTPProvisioningForAuthenticatedUser: InitiateTwoFactorAuthenticationProvisioningOutput; publishServerlessFunction: ServerlessFunction; + removeQueryFromEventStream: Scalars['Boolean']; removeRoleFromAgent: Scalars['Boolean']; renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationTokenOutput; @@ -2008,6 +2028,11 @@ export type MutationActivateWorkspaceArgs = { }; +export type MutationAddQueryToEventStreamArgs = { + input: AddQuerySubscriptionInput; +}; + + export type MutationAssignRoleToAgentArgs = { agentId: Scalars['UUID']; roleId: Scalars['UUID']; @@ -2550,6 +2575,11 @@ export type MutationPublishServerlessFunctionArgs = { }; +export type MutationRemoveQueryFromEventStreamArgs = { + input: RemoveQueryFromEventStreamInput; +}; + + export type MutationRemoveRoleFromAgentArgs = { agentId: Scalars['UUID']; }; @@ -3062,6 +3092,23 @@ export type ObjectPermissionInput = { objectMetadataId: Scalars['UUID']; }; +export type ObjectRecordEvent = { + __typename?: 'ObjectRecordEvent'; + objectNameSingular: Scalars['String']; + properties: ObjectRecordEventProperties; + recordId: Scalars['String']; + userId?: Maybe; + workspaceMemberId?: Maybe; +}; + +export type ObjectRecordEventProperties = { + __typename?: 'ObjectRecordEventProperties'; + after?: Maybe; + before?: Maybe; + diff?: Maybe; + updatedFields?: Maybe>; +}; + export type ObjectRecordFilterInput = { and?: InputMaybe>; createdAt?: InputMaybe; @@ -3798,6 +3845,11 @@ export enum RelationType { ONE_TO_MANY = 'ONE_TO_MANY' } +export type RemoveQueryFromEventStreamInput = { + eventStreamId: Scalars['String']; + queryId: Scalars['String']; +}; + export type ResendEmailVerificationTokenOutput = { __typename?: 'ResendEmailVerificationTokenOutput'; success: Scalars['Boolean']; @@ -4136,6 +4188,7 @@ export type SubmitFormStepInput = { export type Subscription = { __typename?: 'Subscription'; onDbEvent: OnDbEvent; + onEventSubscription?: Maybe; onSubscriptionMatch?: Maybe; serverlessFunctionLogs: ServerlessFunctionLogs; }; @@ -4146,6 +4199,11 @@ export type SubscriptionOnDbEventArgs = { }; +export type SubscriptionOnEventSubscriptionArgs = { + eventStreamId: Scalars['String']; +}; + + export type SubscriptionOnSubscriptionMatchArgs = { subscriptions: Array; }; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 64ac7b0efcc..97aebdd86bf 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -25,6 +25,12 @@ export type ActivateWorkspaceInput = { displayName?: InputMaybe; }; +export type AddQuerySubscriptionInput = { + eventStreamId: Scalars['String']; + operationSignature: Scalars['JSON']; + queryId: Scalars['String']; +}; + export type AdminPanelHealthServiceData = { __typename?: 'AdminPanelHealthServiceData'; description: Scalars['String']; @@ -1294,6 +1300,18 @@ export enum EmailingDomainStatus { VERIFIED = 'VERIFIED' } +export type EventSubscription = { + __typename?: 'EventSubscription'; + eventStreamId: Scalars['String']; + eventWithQueryIdsList: Array; +}; + +export type EventWithQueryIds = { + __typename?: 'EventWithQueryIds'; + event: ObjectRecordEvent; + queryIds: Array; +}; + export type ExecuteServerlessFunctionInput = { /** Id of the serverless function to execute */ id: Scalars['UUID']; @@ -1781,6 +1799,7 @@ export type Mutation = { __typename?: 'Mutation'; activateWorkflowVersion: Scalars['Boolean']; activateWorkspace: Workspace; + addQueryToEventStream: Scalars['Boolean']; assignRoleToAgent: Scalars['Boolean']; assignRoleToApiKey: Scalars['Boolean']; authorizeApp: AuthorizeAppOutput; @@ -1891,6 +1910,7 @@ export type Mutation = { initiateOTPProvisioning: InitiateTwoFactorAuthenticationProvisioningOutput; initiateOTPProvisioningForAuthenticatedUser: InitiateTwoFactorAuthenticationProvisioningOutput; publishServerlessFunction: ServerlessFunction; + removeQueryFromEventStream: Scalars['Boolean']; removeRoleFromAgent: Scalars['Boolean']; renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationTokenOutput; @@ -1978,6 +1998,11 @@ export type MutationActivateWorkspaceArgs = { }; +export type MutationAddQueryToEventStreamArgs = { + input: AddQuerySubscriptionInput; +}; + + export type MutationAssignRoleToAgentArgs = { agentId: Scalars['UUID']; roleId: Scalars['UUID']; @@ -2495,6 +2520,11 @@ export type MutationPublishServerlessFunctionArgs = { }; +export type MutationRemoveQueryFromEventStreamArgs = { + input: RemoveQueryFromEventStreamInput; +}; + + export type MutationRemoveRoleFromAgentArgs = { agentId: Scalars['UUID']; }; @@ -2996,6 +3026,23 @@ export type ObjectPermissionInput = { objectMetadataId: Scalars['UUID']; }; +export type ObjectRecordEvent = { + __typename?: 'ObjectRecordEvent'; + objectNameSingular: Scalars['String']; + properties: ObjectRecordEventProperties; + recordId: Scalars['String']; + userId?: Maybe; + workspaceMemberId?: Maybe; +}; + +export type ObjectRecordEventProperties = { + __typename?: 'ObjectRecordEventProperties'; + after?: Maybe; + before?: Maybe; + diff?: Maybe; + updatedFields?: Maybe>; +}; + export type ObjectRecordFilterInput = { and?: InputMaybe>; createdAt?: InputMaybe; @@ -3673,6 +3720,11 @@ export enum RelationType { ONE_TO_MANY = 'ONE_TO_MANY' } +export type RemoveQueryFromEventStreamInput = { + eventStreamId: Scalars['String']; + queryId: Scalars['String']; +}; + export type ResendEmailVerificationTokenOutput = { __typename?: 'ResendEmailVerificationTokenOutput'; success: Scalars['Boolean']; @@ -4011,6 +4063,7 @@ export type SubmitFormStepInput = { export type Subscription = { __typename?: 'Subscription'; onDbEvent: OnDbEvent; + onEventSubscription?: Maybe; onSubscriptionMatch?: Maybe; serverlessFunctionLogs: ServerlessFunctionLogs; }; @@ -4021,6 +4074,11 @@ export type SubscriptionOnDbEventArgs = { }; +export type SubscriptionOnEventSubscriptionArgs = { + eventStreamId: Scalars['String']; +}; + + export type SubscriptionOnSubscriptionMatchArgs = { subscriptions: Array; }; diff --git a/packages/twenty-front/src/modules/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature.ts b/packages/twenty-front/src/modules/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature.ts index da159d2b710..362f6005e30 100644 --- a/packages/twenty-front/src/modules/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature.ts +++ b/packages/twenty-front/src/modules/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature.ts @@ -1,5 +1,5 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; export const FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE: RecordGqlOperationSignature = { diff --git a/packages/twenty-front/src/modules/activities/hooks/useActivities.ts b/packages/twenty-front/src/modules/activities/hooks/useActivities.ts index bfb8d35e2f0..a2a1bf4cfdc 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useActivities.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useActivities.ts @@ -6,7 +6,7 @@ import { type Task } from '@/activities/types/Task'; import { type TaskTarget } from '@/activities/types/TaskTarget'; import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; +import { type RecordGqlOperationOrderBy } from 'twenty-shared/types'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts b/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts index 2db1e690353..655284c135b 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts @@ -7,7 +7,7 @@ import { type TaskTarget } from '@/activities/types/TaskTarget'; import { getActivityTargetsFilter } from '@/activities/utils/getActivityTargetsFilter'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; +import { type RecordGqlOperationOrderBy } from 'twenty-shared/types'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; export const useActivityTargetsForTargetableObjects = ({ diff --git a/packages/twenty-front/src/modules/activities/notes/hooks/useNotes.ts b/packages/twenty-front/src/modules/activities/notes/hooks/useNotes.ts index da3637ea417..3a0a790d867 100644 --- a/packages/twenty-front/src/modules/activities/notes/hooks/useNotes.ts +++ b/packages/twenty-front/src/modules/activities/notes/hooks/useNotes.ts @@ -5,7 +5,7 @@ import { useActivities } from '@/activities/hooks/useActivities'; import { currentNotesQueryVariablesState } from '@/activities/notes/states/currentNotesQueryVariablesState'; import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy'; import { type Note } from '@/activities/types/Note'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; diff --git a/packages/twenty-front/src/modules/activities/notes/states/currentNotesQueryVariablesState.ts b/packages/twenty-front/src/modules/activities/notes/states/currentNotesQueryVariablesState.ts index 00beb3de70f..1b5bb886962 100644 --- a/packages/twenty-front/src/modules/activities/notes/states/currentNotesQueryVariablesState.ts +++ b/packages/twenty-front/src/modules/activities/notes/states/currentNotesQueryVariablesState.ts @@ -1,6 +1,6 @@ import { atom } from 'recoil'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; export const currentNotesQueryVariablesState = atom({ diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy.ts b/packages/twenty-front/src/modules/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy.ts index 37935ad221d..ec27622d63c 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy.ts +++ b/packages/twenty-front/src/modules/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; +import { type RecordGqlOperationOrderBy } from 'twenty-shared/types'; export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy = [ diff --git a/packages/twenty-front/src/modules/apollo/optimistic-effect/utils/sortCachedObjectEdges.ts b/packages/twenty-front/src/modules/apollo/optimistic-effect/utils/sortCachedObjectEdges.ts index 3242d84c2ac..6fc49057fc8 100644 --- a/packages/twenty-front/src/modules/apollo/optimistic-effect/utils/sortCachedObjectEdges.ts +++ b/packages/twenty-front/src/modules/apollo/optimistic-effect/utils/sortCachedObjectEdges.ts @@ -3,8 +3,11 @@ import { type ReadFieldFunction } from '@apollo/client/cache/core/types/common'; import { isNonEmptyString } from '@sniptt/guards'; import { type RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; -import { type OrderBy } from '@/types/OrderBy'; + +import { + type OrderBy, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { sortAsc, sortDesc, sortNullsFirst, sortNullsLast } from '~/utils/sort'; diff --git a/packages/twenty-front/src/modules/apollo/types/CachedObjectRecordQueryVariables.ts b/packages/twenty-front/src/modules/apollo/types/CachedObjectRecordQueryVariables.ts index 80d32d3bd0a..3d89d26d65b 100644 --- a/packages/twenty-front/src/modules/apollo/types/CachedObjectRecordQueryVariables.ts +++ b/packages/twenty-front/src/modules/apollo/types/CachedObjectRecordQueryVariables.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; export type CachedObjectRecordQueryVariables = Omit< RecordGqlOperationVariables, diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectOrderByField.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectOrderByField.ts index 1eea4037714..4a86be5d398 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectOrderByField.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectOrderByField.ts @@ -1,8 +1,10 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { getOrderByFieldForObjectMetadataItem } from '@/object-metadata/utils/getObjectOrderByField'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; -import { type OrderBy } from '@/types/OrderBy'; +import { + type OrderBy, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; export const useGetObjectOrderByField = ({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts index c5a706f36ca..d840422de96 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts @@ -1,10 +1,12 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField'; import { gql } from '@apollo/client'; -import { type ObjectPermissions } from 'twenty-shared/types'; +import { + type ObjectPermissions, + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; export const generateCreateOneRecordMutation = ({ diff --git a/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts index 2d7bf204076..da158decb32 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts @@ -1,10 +1,12 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField'; import { gql } from '@apollo/client'; -import { type ObjectPermissions } from 'twenty-shared/types'; +import { + type ObjectPermissions, + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; export const generateUpdateOneRecordMutation = ({ diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getObjectOrderByField.ts b/packages/twenty-front/src/modules/object-metadata/utils/getObjectOrderByField.ts index 286c90f1a6a..1911f4d7297 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getObjectOrderByField.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getObjectOrderByField.ts @@ -2,8 +2,10 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem'; import { getOrderByForFieldMetadataType } from '@/object-metadata/utils/getOrderByForFieldMetadataType'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; -import { type OrderBy } from '@/types/OrderBy'; +import { + type OrderBy, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; export const getOrderByFieldForObjectMetadataItem = ( diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getOrderByForFieldMetadataType.ts b/packages/twenty-front/src/modules/object-metadata/utils/getOrderByForFieldMetadataType.ts index 2eeaa33910f..db541aff24c 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getOrderByForFieldMetadataType.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getOrderByForFieldMetadataType.ts @@ -2,13 +2,15 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; import { type FieldEmailsValue, type FieldLinksValue, type FieldPhonesValue, } from '@/object-record/record-field/ui/types/FieldMetadata'; -import { type OrderBy } from '@/types/OrderBy'; +import { + type OrderBy, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; import { FieldMetadataType } from '~/generated-metadata/graphql'; export const getOrderByForFieldMetadataType = ( diff --git a/packages/twenty-front/src/modules/object-metadata/utils/shouldFieldBeQueried.ts b/packages/twenty-front/src/modules/object-metadata/utils/shouldFieldBeQueried.ts index 563c31c038d..88fb6a7f96b 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/shouldFieldBeQueried.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/shouldFieldBeQueried.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts index 814e9342eda..dd4cedd9da4 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts @@ -3,7 +3,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts index 12c7f8d1710..0d318597bdc 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts @@ -4,7 +4,7 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; diff --git a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts index 0a725a0c09c..180ec12fa44 100644 --- a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts +++ b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts @@ -3,7 +3,7 @@ import { getConnectionTypename } from '@/object-record/cache/utils/getConnection import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo'; import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord'; import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; export const getRecordConnectionFromRecords = ({ diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGqlRecordFields.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGqlRecordFields.ts deleted file mode 100644 index 7c6d85e8d3e..00000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGqlRecordFields.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { type RecordGqlFieldsDeprecated } from '@/object-record/graphql/types/RecordGqlFieldsDeprecated'; - -export type RecordGqlOperationGqlRecordFields = RecordGqlFieldsDeprecated; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGroupByVariables.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGroupByVariables.ts index 3f7b712bd4a..eb1e0e84098 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGroupByVariables.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationGroupByVariables.ts @@ -1,5 +1,7 @@ -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; -import { type RecordGqlOperationFilter } from 'twenty-shared/types'; +import { + type RecordGqlOperationFilter, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; export type RecordGqlOperationGroupByVariables = { groupBy: Record[] | Record; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignature.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignature.ts deleted file mode 100644 index 364a8cd1a1a..00000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignature.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; - -export type RecordGqlOperationSignature = { - objectNameSingular: string; - variables: RecordGqlOperationVariables; - fields?: RecordGqlOperationGqlRecordFields; -}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts index 9de77bed800..dac3ea85d89 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; export type RecordGqlOperationSignatureFactory = ( factoryParams: FactoryParams, diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationVariables.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationVariables.ts deleted file mode 100644 index c10ecc52d78..00000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationVariables.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; -import { type QueryCursorDirection } from '@/object-record/utils/generateFindManyRecordsQuery'; -import { type RecordGqlOperationFilter } from 'twenty-shared/types'; - -export type RecordGqlOperationVariables = { - filter?: RecordGqlOperationFilter; - orderBy?: RecordGqlOperationOrderBy; - limit?: number; - cursorFilter?: { - cursor: string; - cursorDirection: QueryCursorDirection; - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts index 0513afd3067..fe6553533d1 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts @@ -12,7 +12,7 @@ import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordF import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts index 546119885b2..82f24197173 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts @@ -5,7 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField'; import { capitalize } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts index 039faa09836..6052964cbc4 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts @@ -13,7 +13,7 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts index eabfb87fe36..f899c50b46d 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts @@ -4,7 +4,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { generateCreateOneRecordMutation } from '@/object-metadata/utils/generateCreateOneRecordMutation'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFetchMoreRecordsWithPagination.ts b/packages/twenty-front/src/modules/object-record/hooks/useFetchMoreRecordsWithPagination.ts index 2fcf6d5dff4..5840efe0f19 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFetchMoreRecordsWithPagination.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFetchMoreRecordsWithPagination.ts @@ -17,18 +17,20 @@ import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabl import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { type RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge'; import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; import { useHandleFindManyRecordsError } from '@/object-record/hooks/useHandleFindManyRecordsError'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted'; import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor'; import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier'; +import { + type RecordGqlOperationGqlRecordFields, + type RecordGqlOperationVariables, +} from 'twenty-shared/types'; -import { capitalize, isDefined } from 'twenty-shared/utils'; import { cursorFamilyState } from '@/object-record/states/cursorFamilyState'; import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState'; import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState'; +import { capitalize, isDefined } from 'twenty-shared/utils'; export type UseFindManyRecordsParams = ObjectMetadataItemIdentifier & RecordGqlOperationVariables & { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts index 16bdb20b740..3d5f56edce2 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts @@ -4,8 +4,6 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier'; import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; import { useFetchMoreRecordsWithPagination } from '@/object-record/hooks/useFetchMoreRecordsWithPagination'; import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; import { useHandleFindManyRecordsCompleted } from '@/object-record/hooks/useHandleFindManyRecordsCompleted'; @@ -14,9 +12,13 @@ import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPe import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted'; import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier'; +import { + type RecordGqlOperationFilter, + type RecordGqlOperationGqlRecordFields, + type RecordGqlOperationVariables, +} from 'twenty-shared/types'; import { QUERY_DEFAULT_LIMIT_RECORDS } from 'twenty-shared/constants'; -import { type RecordGqlOperationFilter } from 'twenty-shared/types'; export type UseFindManyRecordsParams = ObjectMetadataItemIdentifier & RecordGqlOperationVariables & { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts index 6abf13af38e..3483ae22ad3 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts @@ -2,12 +2,12 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; +import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; import { - generateFindManyRecordsQuery, type QueryCursorDirection, -} from '@/object-record/utils/generateFindManyRecordsQuery'; + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; export const useFindManyRecordsQuery = ({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts index 47949e31d1e..13821dcec60 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts @@ -7,7 +7,7 @@ import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/Objec import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts index 784468ea072..6b55734b64f 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts @@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { capitalize } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useGroupByRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useGroupByRecordsQuery.ts index af775369a79..a7aca4367e0 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useGroupByRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useGroupByRecordsQuery.ts @@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { generateGroupByRecordsQuery } from '@/object-record/utils/generateGroupByRecordsQuery'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useLazyFetchMoreRecordsWithPagination.ts b/packages/twenty-front/src/modules/object-record/hooks/useLazyFetchMoreRecordsWithPagination.ts index 1dbf0cd0d93..c8ad297cbb4 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useLazyFetchMoreRecordsWithPagination.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useLazyFetchMoreRecordsWithPagination.ts @@ -16,18 +16,20 @@ import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabl import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { type RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge'; import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; import { useHandleFindManyRecordsError } from '@/object-record/hooks/useHandleFindManyRecordsError'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted'; import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor'; import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier'; +import { + type RecordGqlOperationGqlRecordFields, + type RecordGqlOperationVariables, +} from 'twenty-shared/types'; import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit'; -import { capitalize, isDefined } from 'twenty-shared/utils'; import { cursorFamilyState } from '@/object-record/states/cursorFamilyState'; import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState'; +import { capitalize, isDefined } from 'twenty-shared/utils'; export type UseFindManyRecordsParams = ObjectMetadataItemIdentifier & RecordGqlOperationVariables & { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts index 26d34d59d57..407725bb85f 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts @@ -4,7 +4,7 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier'; import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts index 0bf9bd6c50a..727125029eb 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts @@ -3,7 +3,7 @@ import { useCallback, useState } from 'react'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { useMergeManyRecordsMutation } from '@/object-record/hooks/useMergeManyRecordsMutation'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts index a0682069240..ffddd3e354c 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts @@ -6,7 +6,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getMergeManyRecordsMutationResponseField } from '@/object-record/utils/getMergeManyRecordsMutationResponseField'; import { capitalize } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts index df290afdebb..e8a1bb4444a 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts @@ -4,7 +4,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { generateUpdateOneRecordMutation } from '@/object-metadata/utils/generateUpdateOneRecordMutation'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx index 70c0ab008a1..9f632533b59 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx @@ -4,7 +4,7 @@ import { useSetRecoilState } from 'recoil'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords'; import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedFindManyRecords.ts index 1e8d928886e..ebce1fb9a49 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedFindManyRecords.ts @@ -3,7 +3,7 @@ import { useQuery } from '@apollo/client'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; import { type CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedGetTotalCount.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedGetTotalCount.ts index e6dac3e5017..5303f6783f1 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedGetTotalCount.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useCombinedGetTotalCount.ts @@ -3,7 +3,7 @@ import { useQuery } from '@apollo/client'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; import { type CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts index 04d7108cb35..429064f3532 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts @@ -5,7 +5,7 @@ import { useRecoilValue } from 'recoil'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart'; import isEmpty from 'lodash.isempty'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts index 5c60b137a34..6ea81dc6d35 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts @@ -8,7 +8,7 @@ import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObje import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts index 8429b1b8b57..95b6b4442fe 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts @@ -1,5 +1,5 @@ import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; describe('useCombinedFindManyRecordsQueryVariables', () => { diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables.ts index f067aab636d..5ed73534dee 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; import { isNonEmptyString } from '@sniptt/guards'; import { capitalize, isDefined, isNonEmptyArray } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts index 92401e2c66d..bf8c5fb49b9 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts @@ -1,6 +1,6 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; +import { type RecordGqlOperationOrderBy } from 'twenty-shared/types'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { type EachTestingContext } from 'twenty-shared/testing'; diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts index 91197ae997d..16c71c66df7 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts @@ -1,6 +1,9 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; +import { + type OrderBy, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; import { mapArrayToObject } from '~/utils/array/mapArrayToObject'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -10,7 +13,6 @@ import { } from '@/object-metadata/utils/getOrderByForFieldMetadataType'; import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField'; import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; -import { type OrderBy } from '@/types/OrderBy'; import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType, ViewSortDirection } from '~/generated/graphql'; diff --git a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/generateGroupsRecordsGroupByQuery.ts b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/generateGroupsRecordsGroupByQuery.ts index efed78be062..ba5e32aa52e 100644 --- a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/generateGroupsRecordsGroupByQuery.ts +++ b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/generateGroupsRecordsGroupByQuery.ts @@ -2,9 +2,11 @@ import gql from 'graphql-tag'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { getGroupByQueryResultGqlFieldName } from '@/page-layout/utils/getGroupByQueryResultGqlFieldName'; -import { type ObjectPermissions } from 'twenty-shared/types'; +import { + type ObjectPermissions, + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; export const generateGroupsRecordsGroupByQuery = ({ diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/hooks/useRecordCalendarGroupByRecords.ts b/packages/twenty-front/src/modules/object-record/record-calendar/hooks/useRecordCalendarGroupByRecords.ts index 91ef7d6215b..46b208de1ae 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/hooks/useRecordCalendarGroupByRecords.ts +++ b/packages/twenty-front/src/modules/object-record/record-calendar/hooks/useRecordCalendarGroupByRecords.ts @@ -1,7 +1,6 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; -import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy'; import { useGroupByRecordsQuery } from '@/object-record/hooks/useGroupByRecordsQuery'; import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext'; import { useRecordCalendarQueryDateRangeFilter } from '@/object-record/record-calendar/month/hooks/useRecordCalendarQueryDateRangeFilter'; @@ -14,7 +13,10 @@ import { useQuery } from '@apollo/client'; import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { type Temporal } from 'temporal-polyfill'; -import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; +import { + ObjectRecordGroupByDateGranularity, + type RecordGqlOperationOrderBy, +} from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; export const useRecordCalendarGroupByRecords = ( diff --git a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts index b936e1ddf9f..ef8dbaa4084 100644 --- a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts +++ b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts @@ -9,7 +9,7 @@ import { type SelectableItem } from '@/object-record/select/types/SelectableItem import { getObjectFilterFields } from '@/object-record/select/utils/getObjectFilterFields'; import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables'; import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables'; -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from 'twenty-shared/types'; export const useRecordsForSelect = ({ searchFilterText, diff --git a/packages/twenty-front/src/modules/object-record/types/UseFindManyRecordsParams.ts b/packages/twenty-front/src/modules/object-record/types/UseFindManyRecordsParams.ts index 0d8256b2146..ee73a7f433b 100644 --- a/packages/twenty-front/src/modules/object-record/types/UseFindManyRecordsParams.ts +++ b/packages/twenty-front/src/modules/object-record/types/UseFindManyRecordsParams.ts @@ -1,9 +1,11 @@ import { type WatchQueryFetchPolicy } from '@apollo/client'; import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted'; +import { + type RecordGqlOperationGqlRecordFields, + type RecordGqlOperationVariables, +} from 'twenty-shared/types'; export type UseFindManyRecordsParams = ObjectMetadataItemIdentifier & RecordGqlOperationVariables & { diff --git a/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts index 19547e3ab2c..0a2d32f4dd7 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts @@ -2,12 +2,13 @@ import gql from 'graphql-tag'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type ObjectPermissions } from 'twenty-shared/types'; +import { + type ObjectPermissions, + type QueryCursorDirection, + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; -export type QueryCursorDirection = 'before' | 'after'; - export const generateFindManyRecordsQuery = ({ objectMetadataItem, objectMetadataItems, diff --git a/packages/twenty-front/src/modules/object-record/utils/generateGroupByRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/utils/generateGroupByRecordsQuery.ts index 26c21d8bccf..ffb7a7f90ca 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateGroupByRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateGroupByRecordsQuery.ts @@ -2,8 +2,10 @@ import { gql } from '@apollo/client'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { type ObjectPermissions } from 'twenty-shared/types'; +import { + type ObjectPermissions, + type RecordGqlOperationGqlRecordFields, +} from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; export const generateGroupByRecordsQuery = ({ diff --git a/packages/twenty-front/src/modules/object-record/utils/getQueryIdentifier.ts b/packages/twenty-front/src/modules/object-record/utils/getQueryIdentifier.ts index 7838fed7ae7..fcf19cf5dda 100644 --- a/packages/twenty-front/src/modules/object-record/utils/getQueryIdentifier.ts +++ b/packages/twenty-front/src/modules/object-record/utils/getQueryIdentifier.ts @@ -1,4 +1,4 @@ -import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables'; +import { type RecordGqlOperationVariables } from 'twenty-shared/types'; export const getQueryIdentifier = ({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx index eefff93931d..48daf309a55 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx @@ -7,7 +7,7 @@ import { } from '@/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTableRow'; import { useObjectPermissionDerivedStates } from '@/settings/roles/role-permissions/object-level-permissions/field-permissions/hooks/useObjectPermissionDerivedStates'; import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from 'twenty-shared/types'; import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput'; import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader'; import { Table } from '@/ui/layout/table/components/Table'; diff --git a/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx b/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx index cf9ab8eec1d..aa4a7ec8435 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx @@ -12,7 +12,7 @@ import { tableDataSortedBylabelInDescendingOrder, } from '~/testing/mock-data/tableData'; -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from 'twenty-shared/types'; import { sortedFieldByTableFamilyState } from '@/ui/layout/table/states/sortedFieldByTableFamilyState'; import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray'; diff --git a/packages/twenty-front/src/modules/ui/layout/table/types/TableSortValue.ts b/packages/twenty-front/src/modules/ui/layout/table/types/TableSortValue.ts index bff5a0095d9..ce319ee1085 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/types/TableSortValue.ts +++ b/packages/twenty-front/src/modules/ui/layout/table/types/TableSortValue.ts @@ -1,4 +1,4 @@ -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from 'twenty-shared/types'; export type TableSortValue = { fieldName: string; diff --git a/packages/twenty-front/src/utils/turnOrderByIntoSort.ts b/packages/twenty-front/src/utils/turnOrderByIntoSort.ts index f4c3a9f6a66..d538095a0c9 100644 --- a/packages/twenty-front/src/utils/turnOrderByIntoSort.ts +++ b/packages/twenty-front/src/utils/turnOrderByIntoSort.ts @@ -1,4 +1,4 @@ -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from 'twenty-shared/types'; import { assertUnreachable } from 'twenty-shared/utils'; export const turnOrderByIntoSort = (orderBy: OrderBy): 'asc' | 'desc' => { diff --git a/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts b/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts index 983c5db471b..b4725ee86b0 100644 --- a/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum.ts @@ -5,4 +5,5 @@ export enum CacheStorageNamespace { EngineWorkspace = 'engine:workspace', EngineLock = 'engine:lock', EngineHealth = 'engine:health', + EngineSubscriptions = 'engine:subscriptions', } diff --git a/packages/twenty-server/src/engine/subscriptions/dtos/add-query-subscription.input.ts b/packages/twenty-server/src/engine/subscriptions/dtos/add-query-subscription.input.ts new file mode 100644 index 00000000000..660fa8cfc8b --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/dtos/add-query-subscription.input.ts @@ -0,0 +1,16 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import GraphQLJSON from 'graphql-type-json'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; + +@InputType() +export class AddQuerySubscriptionInput { + @Field() + eventStreamId: string; + + @Field() + queryId: string; + + @Field(() => GraphQLJSON) + operationSignature: RecordGqlOperationSignature; +} diff --git a/packages/twenty-server/src/engine/subscriptions/dtos/event-subscription.dto.ts b/packages/twenty-server/src/engine/subscriptions/dtos/event-subscription.dto.ts new file mode 100644 index 00000000000..4fd5e57dbce --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/dtos/event-subscription.dto.ts @@ -0,0 +1,21 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import { ObjectRecordEventDTO } from './object-record-event.dto'; + +@ObjectType('EventWithQueryIds') +export class EventWithQueryIdsDTO { + @Field(() => [String]) + queryIds: string[]; + + @Field(() => ObjectRecordEventDTO) + event: ObjectRecordEventDTO; +} + +@ObjectType('EventSubscription') +export class EventSubscriptionDTO { + @Field(() => String) + eventStreamId: string; + + @Field(() => [EventWithQueryIdsDTO]) + eventWithQueryIdsList: EventWithQueryIdsDTO[]; +} diff --git a/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event-properties.dto.ts b/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event-properties.dto.ts new file mode 100644 index 00000000000..a082e4fa31f --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event-properties.dto.ts @@ -0,0 +1,18 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import GraphQLJSON from 'graphql-type-json'; + +@ObjectType('ObjectRecordEventProperties') +export class ObjectRecordEventPropertiesDTO { + @Field(() => [String], { nullable: true }) + updatedFields?: string[]; + + @Field(() => GraphQLJSON, { nullable: true }) + before?: object; + + @Field(() => GraphQLJSON, { nullable: true }) + after?: object; + + @Field(() => GraphQLJSON, { nullable: true }) + diff?: object; +} diff --git a/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event.dto.ts b/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event.dto.ts new file mode 100644 index 00000000000..d19e95fcd17 --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/dtos/object-record-event.dto.ts @@ -0,0 +1,21 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import { ObjectRecordEventPropertiesDTO } from 'src/engine/subscriptions/dtos/object-record-event-properties.dto'; + +@ObjectType('ObjectRecordEvent') +export class ObjectRecordEventDTO { + @Field(() => String) + objectNameSingular: string; + + @Field(() => String) + recordId: string; + + @Field(() => String, { nullable: true }) + userId?: string; + + @Field(() => String, { nullable: true }) + workspaceMemberId?: string; + + @Field(() => ObjectRecordEventPropertiesDTO) + properties: ObjectRecordEventPropertiesDTO; +} diff --git a/packages/twenty-server/src/engine/subscriptions/dtos/remove-query-subscription.input.ts b/packages/twenty-server/src/engine/subscriptions/dtos/remove-query-subscription.input.ts new file mode 100644 index 00000000000..fd4fe773272 --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/dtos/remove-query-subscription.input.ts @@ -0,0 +1,10 @@ +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class RemoveQueryFromEventStreamInput { + @Field() + eventStreamId: string; + + @Field() + queryId: string; +} diff --git a/packages/twenty-server/src/engine/subscriptions/enums/subscription-channel.enum.ts b/packages/twenty-server/src/engine/subscriptions/enums/subscription-channel.enum.ts index cc3047530c1..6c5da5a6970 100644 --- a/packages/twenty-server/src/engine/subscriptions/enums/subscription-channel.enum.ts +++ b/packages/twenty-server/src/engine/subscriptions/enums/subscription-channel.enum.ts @@ -1,5 +1,5 @@ export enum SubscriptionChannel { + WORKSPACE_EVENT_BATCH_CHANNEL = 'WORKSPACE_EVENT_BATCH_CHANNEL', DATABASE_EVENT_CHANNEL = 'DATABASE_EVENT_CHANNEL', - DATABASE_BATCH_EVENTS_CHANNEL = 'DATABASE_BATCH_EVENTS_CHANNEL', SERVERLESS_FUNCTION_LOGS_CHANNEL = 'SERVERLESS_FUNCTION_LOGS_CHANNEL', } diff --git a/packages/twenty-server/src/engine/subscriptions/event-stream.service.ts b/packages/twenty-server/src/engine/subscriptions/event-stream.service.ts new file mode 100644 index 00000000000..538af3264ed --- /dev/null +++ b/packages/twenty-server/src/engine/subscriptions/event-stream.service.ts @@ -0,0 +1,109 @@ +import { Injectable } from '@nestjs/common'; + +import { type ObjectRecordEvent } from 'twenty-shared/database-events'; +import { type RecordGqlOperationSignature } from 'twenty-shared/types'; + +import { WithLock } from 'src/engine/core-modules/cache-lock/with-lock.decorator'; +import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator'; +import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service'; +import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum'; + +export type ObjectRecordSubscriptionEvent = ObjectRecordEvent & { + objectNameSingular: string; +}; + +@Injectable() +export class EventStreamService { + constructor( + @InjectCacheStorage(CacheStorageNamespace.EngineSubscriptions) + private readonly cacheStorageService: CacheStorageService, + ) {} + + private getEventStreamKey( + workspaceId: string, + eventStreamId: string, + ): string { + return `eventStream:${workspaceId}:${eventStreamId}`; + } + + @WithLock('eventStreamId') + async addQuery({ + workspaceId, + eventStreamId, + queryId, + operationSignature, + }: { + workspaceId: string; + eventStreamId: string; + queryId: string; + operationSignature: RecordGqlOperationSignature; + }): Promise { + const key = this.getEventStreamKey(workspaceId, eventStreamId); + const existing = + (await this.cacheStorageService.get< + Record + >(key)) || {}; + + existing[queryId] = operationSignature; + + await this.cacheStorageService.set(key, existing); + } + + @WithLock('eventStreamId') + async removeQuery({ + workspaceId, + eventStreamId, + queryId, + }: { + workspaceId: string; + eventStreamId: string; + queryId: string; + }): Promise { + const key = this.getEventStreamKey(workspaceId, eventStreamId); + const existing = + await this.cacheStorageService.get< + Record + >(key); + + if (existing && existing[queryId]) { + delete existing[queryId]; + await this.cacheStorageService.set(key, existing); + } + } + + async getQueries( + workspaceId: string, + eventStreamId: string, + ): Promise> { + const key = this.getEventStreamKey(workspaceId, eventStreamId); + const data = + await this.cacheStorageService.get< + Record + >(key); + + return new Map(Object.entries(data || {})); + } + + async matchQueriesWithEvent( + queries: Map, + event: ObjectRecordSubscriptionEvent, + ): Promise { + const matchedQueryIds: string[] = []; + + for (const [queryId, operationSignature] of queries.entries()) { + if (this.isQueryMatchingEvent(operationSignature, event)) { + matchedQueryIds.push(queryId); + } + } + + return matchedQueryIds; + } + + private isQueryMatchingEvent( + operationSignature: RecordGqlOperationSignature, + event: ObjectRecordSubscriptionEvent, + ): boolean { + // to be improved + return operationSignature.objectNameSingular === event.objectNameSingular; + } +} diff --git a/packages/twenty-server/src/engine/subscriptions/subscriptions.module.ts b/packages/twenty-server/src/engine/subscriptions/subscriptions.module.ts index 8c57e092f55..7ee227f1f01 100644 --- a/packages/twenty-server/src/engine/subscriptions/subscriptions.module.ts +++ b/packages/twenty-server/src/engine/subscriptions/subscriptions.module.ts @@ -1,12 +1,20 @@ import { Module } from '@nestjs/common'; +import { CacheLockModule } from 'src/engine/core-modules/cache-lock/cache-lock.module'; +import { CacheStorageModule } from 'src/engine/core-modules/cache-storage/cache-storage.module'; import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module'; import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; +import { EventStreamService } from 'src/engine/subscriptions/event-stream.service'; import { SubscriptionService } from 'src/engine/subscriptions/subscription.service'; @Module({ - imports: [RedisClientModule, WorkspaceManyOrAllFlatEntityMapsCacheModule], - providers: [SubscriptionService], - exports: [SubscriptionService], + imports: [ + RedisClientModule, + WorkspaceManyOrAllFlatEntityMapsCacheModule, + CacheStorageModule, + CacheLockModule, + ], + providers: [SubscriptionService, EventStreamService], + exports: [SubscriptionService, EventStreamService], }) export class SubscriptionsModule {} diff --git a/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.resolver.ts b/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.resolver.ts index 199c9b84432..bcfe672e479 100644 --- a/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.resolver.ts +++ b/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.resolver.ts @@ -1,6 +1,7 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; -import { Args, Resolver, Subscription } from '@nestjs/graphql'; +import { Args, Mutation, Resolver, Subscription } from '@nestjs/graphql'; +import { ObjectRecordEvent } from 'twenty-shared/database-events'; import { isDefined } from 'twenty-shared/utils'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; @@ -10,19 +11,28 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; +import { AddQuerySubscriptionInput } from 'src/engine/subscriptions/dtos/add-query-subscription.input'; +import { EventSubscriptionDTO } from 'src/engine/subscriptions/dtos/event-subscription.dto'; +import { ObjectRecordEventDTO } from 'src/engine/subscriptions/dtos/object-record-event.dto'; import { OnDbEventDTO } from 'src/engine/subscriptions/dtos/on-db-event.dto'; import { OnDbEventInput } from 'src/engine/subscriptions/dtos/on-db-event.input'; +import { RemoveQueryFromEventStreamInput } from 'src/engine/subscriptions/dtos/remove-query-subscription.input'; import { SubscriptionMatchesDTO } from 'src/engine/subscriptions/dtos/subscription-matches.dto'; import { SubscriptionInput } from 'src/engine/subscriptions/dtos/subscription.input'; import { SubscriptionChannel } from 'src/engine/subscriptions/enums/subscription-channel.enum'; +import { EventStreamService } from 'src/engine/subscriptions/event-stream.service'; import { SubscriptionService } from 'src/engine/subscriptions/subscription.service'; +import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type'; @Resolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard, NoPermissionGuard) @UsePipes(ResolverValidationPipe) @UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) export class WorkspaceEventEmitterResolver { - constructor(private readonly subscriptionService: SubscriptionService) {} + constructor( + private readonly subscriptionService: SubscriptionService, + private readonly eventStreamService: EventStreamService, + ) {} @Subscription(() => OnDbEventDTO, { filter: ( @@ -104,8 +114,94 @@ export class WorkspaceEventEmitterResolver { @AuthWorkspace() workspace: WorkspaceEntity, ) { return this.subscriptionService.subscribe({ - channel: SubscriptionChannel.DATABASE_BATCH_EVENTS_CHANNEL, + channel: SubscriptionChannel.WORKSPACE_EVENT_BATCH_CHANNEL, workspaceId: workspace.id, }); } + + @Subscription(() => EventSubscriptionDTO, { + nullable: true, + resolve: async function ( + this: WorkspaceEventEmitterResolver, + payload: { workspaceEventBatch: WorkspaceEventBatch }, + args: { eventStreamId: string }, + context: { req: { workspace: { id: string } } }, + ): Promise { + const workspaceId = context.req.workspace.id; + const { eventStreamId } = args; + + const queries = await this.eventStreamService.getQueries( + workspaceId, + eventStreamId, + ); + + const objectNameSingular = + payload.workspaceEventBatch.objectMetadata.nameSingular; + + const eventWithQueryIdsList: { + queryIds: string[]; + event: ObjectRecordEventDTO; + }[] = []; + + for (const event of payload.workspaceEventBatch.events) { + const eventWithObjectName = { + objectNameSingular, + ...event, + }; + + const matchedQueryIds = + await this.eventStreamService.matchQueriesWithEvent( + queries, + eventWithObjectName, + ); + + if (matchedQueryIds.length > 0) { + eventWithQueryIdsList.push({ + queryIds: matchedQueryIds, + event: eventWithObjectName, + }); + } + } + + return { eventStreamId, eventWithQueryIdsList }; + }, + }) + onEventSubscription( + @Args('eventStreamId') _: string, + @AuthWorkspace() workspace: WorkspaceEntity, + ) { + return this.subscriptionService.subscribe({ + channel: SubscriptionChannel.WORKSPACE_EVENT_BATCH_CHANNEL, + workspaceId: workspace.id, + }); + } + + @Mutation(() => Boolean) + async addQueryToEventStream( + @Args('input') input: AddQuerySubscriptionInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + await this.eventStreamService.addQuery({ + workspaceId: workspace.id, + eventStreamId: input.eventStreamId, + queryId: input.queryId, + operationSignature: input.operationSignature, + }); + + return true; + } + + @Mutation(() => Boolean) + async removeQueryFromEventStream( + @Args('input') input: RemoveQueryFromEventStreamInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + await this.eventStreamService.removeQuery({ + workspaceId: workspace.id, + eventStreamId: input.eventStreamId, + queryId: input.queryId, + }); + + return true; + } } diff --git a/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.service.ts b/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.service.ts index 1a570c150e5..522cd225899 100644 --- a/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.service.ts +++ b/packages/twenty-server/src/engine/workspace-event-emitter/workspace-event-emitter.service.ts @@ -43,9 +43,9 @@ export class WorkspaceEventEmitterService { } await this.subscriptionService.publish({ - channel: SubscriptionChannel.DATABASE_BATCH_EVENTS_CHANNEL, + channel: SubscriptionChannel.WORKSPACE_EVENT_BATCH_CHANNEL, workspaceId: workspaceEventBatch.workspaceId, - payload: { onDbEvents: batchEvents }, + payload: { workspaceEventBatch: workspaceEventBatch }, }); } } diff --git a/packages/twenty-front/src/modules/types/OrderBy.ts b/packages/twenty-shared/src/types/OrderBy.ts similarity index 100% rename from packages/twenty-front/src/modules/types/OrderBy.ts rename to packages/twenty-shared/src/types/OrderBy.ts diff --git a/packages/twenty-shared/src/types/QueryCursorDirection.ts b/packages/twenty-shared/src/types/QueryCursorDirection.ts new file mode 100644 index 00000000000..637764de635 --- /dev/null +++ b/packages/twenty-shared/src/types/QueryCursorDirection.ts @@ -0,0 +1 @@ +export type QueryCursorDirection = 'before' | 'after'; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlFieldsDeprecated.ts b/packages/twenty-shared/src/types/RecordGqlFieldsDeprecated.ts similarity index 100% rename from packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlFieldsDeprecated.ts rename to packages/twenty-shared/src/types/RecordGqlFieldsDeprecated.ts diff --git a/packages/twenty-shared/src/types/RecordGqlOperationGqlRecordFields.ts b/packages/twenty-shared/src/types/RecordGqlOperationGqlRecordFields.ts new file mode 100644 index 00000000000..de49b0c2560 --- /dev/null +++ b/packages/twenty-shared/src/types/RecordGqlOperationGqlRecordFields.ts @@ -0,0 +1,3 @@ +import { type RecordGqlFieldsDeprecated } from './RecordGqlFieldsDeprecated'; + +export type RecordGqlOperationGqlRecordFields = RecordGqlFieldsDeprecated; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationOrderBy.ts b/packages/twenty-shared/src/types/RecordGqlOperationOrderBy.ts similarity index 91% rename from packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationOrderBy.ts rename to packages/twenty-shared/src/types/RecordGqlOperationOrderBy.ts index 783e0d63e1c..dd0384f9997 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationOrderBy.ts +++ b/packages/twenty-shared/src/types/RecordGqlOperationOrderBy.ts @@ -1,4 +1,4 @@ -import { type OrderBy } from '@/types/OrderBy'; +import { type OrderBy } from './OrderBy'; // Recursive type for nested orderBy values // Supports: OrderBy | { field: OrderBy } | { field: { subField: OrderBy } } | ... diff --git a/packages/twenty-shared/src/types/RecordGqlOperationSignature.ts b/packages/twenty-shared/src/types/RecordGqlOperationSignature.ts new file mode 100644 index 00000000000..3e9d7654253 --- /dev/null +++ b/packages/twenty-shared/src/types/RecordGqlOperationSignature.ts @@ -0,0 +1,8 @@ +import { type RecordGqlOperationGqlRecordFields } from './RecordGqlOperationGqlRecordFields'; +import { type RecordGqlOperationVariables } from './RecordGqlOperationVariables'; + +export type RecordGqlOperationSignature = { + objectNameSingular: string; + variables: RecordGqlOperationVariables; + fields?: RecordGqlOperationGqlRecordFields; +}; diff --git a/packages/twenty-shared/src/types/RecordGqlOperationVariables.ts b/packages/twenty-shared/src/types/RecordGqlOperationVariables.ts new file mode 100644 index 00000000000..5d33bf97ab3 --- /dev/null +++ b/packages/twenty-shared/src/types/RecordGqlOperationVariables.ts @@ -0,0 +1,13 @@ +import { type QueryCursorDirection } from './QueryCursorDirection'; +import { type RecordGqlOperationFilter } from './RecordGqlOperationFilter'; +import { type RecordGqlOperationOrderBy } from './RecordGqlOperationOrderBy'; + +export type RecordGqlOperationVariables = { + filter?: RecordGqlOperationFilter; + orderBy?: RecordGqlOperationOrderBy; + limit?: number; + cursorFilter?: { + cursor: string; + cursorDirection: QueryCursorDirection; + }; +}; diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts index 059911e523c..d7d3e593875 100644 --- a/packages/twenty-shared/src/types/index.ts +++ b/packages/twenty-shared/src/types/index.ts @@ -137,10 +137,13 @@ export { OrderByDirection } from './ObjectRecordGroupBy'; export { ObjectRecordGroupByDateGranularity } from './ObjectRecordGroupByDateGranularity'; export type { ObjectsPermissions } from './ObjectsPermissions'; export type { ObjectsPermissionsByRoleId } from './ObjectsPermissionsByRoleId'; +export type { OrderBy } from './OrderBy'; export type { PartialFieldMetadataItem } from './PartialFieldMetadataItem'; export type { PartialFieldMetadataItemOption } from './PartialFieldMetadataOption'; +export type { QueryCursorDirection } from './QueryCursorDirection'; export { RecordFilterGroupLogicalOperator } from './RecordFilterGroupLogicalOperator'; export type { RecordFilterValueDependencies } from './RecordFilterValueDependencies'; +export type { RecordGqlFieldsDeprecated } from './RecordGqlFieldsDeprecated'; export type { UUIDFilterValue, IsFilter, @@ -174,6 +177,10 @@ export type { LeafObjectRecordFilter, RecordGqlOperationFilter, } from './RecordGqlOperationFilter'; +export type { RecordGqlOperationGqlRecordFields } from './RecordGqlOperationGqlRecordFields'; +export type { RecordGqlOperationOrderBy } from './RecordGqlOperationOrderBy'; +export type { RecordGqlOperationSignature } from './RecordGqlOperationSignature'; +export type { RecordGqlOperationVariables } from './RecordGqlOperationVariables'; export type { RelationAndMorphRelationFieldMetadataType } from './RelationAndMorphRelationFieldMetadataType'; export type { RelationCreationPayload } from './RelationCreationPayload'; export { RelationOnDeleteAction } from './RelationOnDeleteAction.type';