From 1d56ace2af91a1be117634a03f0ed1b36794bff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:46:39 +0200 Subject: [PATCH] Fix sync statuses on the fe (#7117) Follows #6685 --- .../src/generated-metadata/graphql.ts | 6 +- .../twenty-front/src/generated/graphql.tsx | 2 - .../modules/accounts/types/CalendarChannel.ts | 34 +++++++- .../modules/accounts/types/MessageChannel.ts | 25 +++++- ...untsConnectedAccountsRowRightContainer.tsx | 36 +++----- .../settings/accounts/constants/SyncStatus.ts | 6 ++ .../utils/__tests__/computeSyncStatus.test.ts | 87 +++++++++++++++++++ .../accounts/utils/computeSyncStatus.ts | 42 +++++++++ .../dtos/timeline-calendar-event.dto.ts | 7 +- .../messaging/dtos/timeline-thread.dto.ts | 7 +- .../calendar-channel.workspace-entity.ts | 18 ++++ .../message-channel.workspace-entity.ts | 22 +++++ 12 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/accounts/constants/SyncStatus.ts create mode 100644 packages/twenty-front/src/modules/settings/accounts/utils/__tests__/computeSyncStatus.test.ts create mode 100644 packages/twenty-front/src/modules/settings/accounts/utils/computeSyncStatus.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 23d7caab7..ce122e418 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -137,7 +137,6 @@ export type BooleanFieldComparison = { isNot?: InputMaybe; }; -/** Visibility of the calendar channel */ export enum CalendarChannelVisibility { Metadata = 'METADATA', ShareEverything = 'SHARE_EVERYTHING' @@ -355,6 +354,7 @@ export type FieldConnection = { export enum FieldMetadataType { Actor = 'ACTOR', Address = 'ADDRESS', + Array = 'ARRAY', Boolean = 'BOOLEAN', Currency = 'CURRENCY', Date = 'DATE', @@ -376,8 +376,7 @@ export enum FieldMetadataType { RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', - Uuid = 'UUID', - Array = 'ARRAY' + Uuid = 'UUID' } export enum FileFolder { @@ -432,7 +431,6 @@ export type LoginToken = { loginToken: AuthToken; }; -/** Visibility of the message channel */ export enum MessageChannelVisibility { Metadata = 'METADATA', ShareEverything = 'SHARE_EVERYTHING', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 88b52322e..ee913c8bb 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -130,7 +130,6 @@ export type BooleanFieldComparison = { isNot?: InputMaybe; }; -/** Visibility of the calendar channel */ export enum CalendarChannelVisibility { Metadata = 'METADATA', ShareEverything = 'SHARE_EVERYTHING' @@ -330,7 +329,6 @@ export type LoginToken = { loginToken: AuthToken; }; -/** Visibility of the message channel */ export enum MessageChannelVisibility { Metadata = 'METADATA', ShareEverything = 'SHARE_EVERYTHING', diff --git a/packages/twenty-front/src/modules/accounts/types/CalendarChannel.ts b/packages/twenty-front/src/modules/accounts/types/CalendarChannel.ts index 21915d2cd..9664352e7 100644 --- a/packages/twenty-front/src/modules/accounts/types/CalendarChannel.ts +++ b/packages/twenty-front/src/modules/accounts/types/CalendarChannel.ts @@ -1,11 +1,39 @@ import { CalendarChannelVisibility } from '~/generated/graphql'; +export enum CalendarChannelSyncStatus { + NOT_SYNCED = 'NOT_SYNCED', + ONGOING = 'ONGOING', + ACTIVE = 'ACTIVE', + FAILED_INSUFFICIENT_PERMISSIONS = 'FAILED_INSUFFICIENT_PERMISSIONS', + FAILED_UNKNOWN = 'FAILED_UNKNOWN', +} + +export enum CalendarChannelSyncStage { + FULL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'FULL_CALENDAR_EVENT_LIST_FETCH_PENDING', + PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING', + CALENDAR_EVENT_LIST_FETCH_ONGOING = 'CALENDAR_EVENT_LIST_FETCH_ONGOING', + CALENDAR_EVENTS_IMPORT_PENDING = 'CALENDAR_EVENTS_IMPORT_PENDING', + CALENDAR_EVENTS_IMPORT_ONGOING = 'CALENDAR_EVENTS_IMPORT_ONGOING', + FAILED = 'FAILED', +} + +export enum CalendarChannelContactAutoCreationPolicy { + AS_PARTICIPANT_AND_ORGANIZER = 'AS_PARTICIPANT_AND_ORGANIZER', + AS_PARTICIPANT = 'AS_PARTICIPANT', + AS_ORGANIZER = 'AS_ORGANIZER', + NONE = 'NONE', +} export type CalendarChannel = { id: string; handle: string; - isContactAutoCreationEnabled?: boolean; - isSyncEnabled?: boolean; + isContactAutoCreationEnabled: boolean; + contactAutoCreationPolicy: CalendarChannelContactAutoCreationPolicy; + isSyncEnabled: boolean; visibility: CalendarChannelVisibility; - syncStatus: string; + syncStatus: CalendarChannelSyncStatus; + syncStage: CalendarChannelSyncStage; + syncCursor: string; + syncStageStartedAt: Date; + throttleFailureCount: number; __typename: 'CalendarChannel'; }; diff --git a/packages/twenty-front/src/modules/accounts/types/MessageChannel.ts b/packages/twenty-front/src/modules/accounts/types/MessageChannel.ts index 33df519b4..87d775283 100644 --- a/packages/twenty-front/src/modules/accounts/types/MessageChannel.ts +++ b/packages/twenty-front/src/modules/accounts/types/MessageChannel.ts @@ -6,14 +6,35 @@ export enum MessageChannelContactAutoCreationPolicy { NONE = 'NONE', } +export enum MessageChannelSyncStatus { + NOT_SYNCED = 'NOT_SYNCED', + ONGOING = 'ONGOING', + ACTIVE = 'ACTIVE', + FAILED_INSUFFICIENT_PERMISSIONS = 'FAILED_INSUFFICIENT_PERMISSIONS', + FAILED_UNKNOWN = 'FAILED_UNKNOWN', +} + +export enum MessageChannelSyncStage { + FULL_MESSAGE_LIST_FETCH_PENDING = 'FULL_MESSAGE_LIST_FETCH_PENDING', + PARTIAL_MESSAGE_LIST_FETCH_PENDING = 'PARTIAL_MESSAGE_LIST_FETCH_PENDING', + MESSAGE_LIST_FETCH_ONGOING = 'MESSAGE_LIST_FETCH_ONGOING', + MESSAGES_IMPORT_PENDING = 'MESSAGES_IMPORT_PENDING', + MESSAGES_IMPORT_ONGOING = 'MESSAGES_IMPORT_ONGOING', + FAILED = 'FAILED', +} + export type MessageChannel = { id: string; handle: string; - contactAutoCreationPolicy?: MessageChannelContactAutoCreationPolicy; + contactAutoCreationPolicy: MessageChannelContactAutoCreationPolicy; excludeNonProfessionalEmails: boolean; excludeGroupEmails: boolean; isSyncEnabled: boolean; visibility: MessageChannelVisibility; - syncStatus: string; + syncStatus: MessageChannelSyncStatus; + syncStage: MessageChannelSyncStage; + syncCursor: string; + syncStageStartedAt: Date; + throttleFailureCount: number; __typename: 'MessageChannel'; }; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer.tsx index 2d05e3752..2091be236 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer.tsx @@ -1,8 +1,9 @@ import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu'; +import { SyncStatus } from '@/settings/accounts/constants/SyncStatus'; +import { computeSyncStatus } from '@/settings/accounts/utils/computeSyncStatus'; import { Status } from '@/ui/display/status/components/Status'; import styled from '@emotion/styled'; -import { useMemo } from 'react'; const StyledRowRightContainer = styled.div` align-items: center; @@ -15,39 +16,26 @@ export const SettingsAccountsConnectedAccountsRowRightContainer = ({ }: { account: ConnectedAccount; }) => { - const mCSyncStatus = account.messageChannels[0]?.syncStatus; - const cCSyncStatus = account.calendarChannels[0]?.syncStatus; + const messageChannelSyncStatus = account.messageChannels[0]?.syncStatus; + const calendarChannelSyncStatus = account.calendarChannels[0]?.syncStatus; - const status = useMemo(() => { - if (mCSyncStatus === 'ACTIVE' && cCSyncStatus === 'ACTIVE') { - return 'Synced'; - } else if (mCSyncStatus === 'NOT_SYNCED' && cCSyncStatus === 'NOT_SYNCED') { - return 'Not synced'; - } else if (mCSyncStatus === 'ONGOING' || cCSyncStatus === 'ONGOING') { - return 'Importing'; - } else if ( - mCSyncStatus === 'FAILED' || - mCSyncStatus === 'FAILED_INSUFFICIENT_PERMISSIONS' || - cCSyncStatus === 'FAILED' || - cCSyncStatus === 'FAILED_INSUFFICIENT_PERMISSIONS' - ) { - return 'Failed'; - } - return ''; - }, [mCSyncStatus, cCSyncStatus]); + const status = computeSyncStatus( + messageChannelSyncStatus, + calendarChannelSyncStatus, + ); return ( - {status === 'Failed' && ( + {status === SyncStatus.FAILED && ( )} - {status === 'Synced' && ( + {status === SyncStatus.SYNCED && ( )} - {status === 'Not synced' && ( + {status === SyncStatus.NOT_SYNCED && ( )} - {status === 'Importing' && ( + {status === SyncStatus.IMPORTING && ( { + test('should return FAILED when both sync statuses are FAILED', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.FAILED_UNKNOWN, + CalendarChannelSyncStatus.FAILED_UNKNOWN, + ), + ).toEqual(SyncStatus.FAILED); + }); + + test('should return FAILED when message channel sync status is FAILED_UNKNOWN', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.FAILED_UNKNOWN, + CalendarChannelSyncStatus.ACTIVE, + ), + ).toEqual(SyncStatus.FAILED); + }); + + test('should return FAILED when message channel sync status is FAILED_INSUFFICIENT_PERMISSIONS', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS, + CalendarChannelSyncStatus.ACTIVE, + ), + ).toEqual(SyncStatus.FAILED); + }); + + test('should return FAILED when calendar channel sync status is FAILED_UNKNOWN', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.ACTIVE, + CalendarChannelSyncStatus.FAILED_UNKNOWN, + ), + ).toEqual(SyncStatus.FAILED); + }); + + test('should return FAILED when calendar channel sync status is FAILED_INSUFFICIENT_PERMISSIONS', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.ACTIVE, + CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS, + ), + ).toEqual(SyncStatus.FAILED); + }); + + test('should return IMPORTING when message channel sync status is ONGOING', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.ONGOING, + CalendarChannelSyncStatus.ACTIVE, + ), + ).toEqual(SyncStatus.IMPORTING); + }); + + test('should return IMPORTING when calendar channel sync status is ONGOING', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.ACTIVE, + CalendarChannelSyncStatus.ONGOING, + ), + ).toEqual(SyncStatus.IMPORTING); + }); + + test('should return SYNCED when one channel is ACTIVE and the other is NOT_SYNCED', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.NOT_SYNCED, + CalendarChannelSyncStatus.ACTIVE, + ), + ).toEqual(SyncStatus.SYNCED); + }); + + test('should return SYNCED when one channel is ACTIVE and the other is NOT_SYNCED', () => { + expect( + computeSyncStatus( + MessageChannelSyncStatus.ACTIVE, + CalendarChannelSyncStatus.NOT_SYNCED, + ), + ).toEqual(SyncStatus.SYNCED); + }); +}); diff --git a/packages/twenty-front/src/modules/settings/accounts/utils/computeSyncStatus.ts b/packages/twenty-front/src/modules/settings/accounts/utils/computeSyncStatus.ts new file mode 100644 index 000000000..5528535f6 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/accounts/utils/computeSyncStatus.ts @@ -0,0 +1,42 @@ +import { CalendarChannelSyncStatus } from '@/accounts/types/CalendarChannel'; +import { MessageChannelSyncStatus } from '@/accounts/types/MessageChannel'; +import { SyncStatus } from '@/settings/accounts/constants/SyncStatus'; + +export const computeSyncStatus = ( + messageChannelSyncStatus: MessageChannelSyncStatus, + calendarChannelSyncStatus: CalendarChannelSyncStatus, +) => { + if ( + messageChannelSyncStatus === MessageChannelSyncStatus.FAILED_UNKNOWN || + messageChannelSyncStatus === + MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS || + calendarChannelSyncStatus === CalendarChannelSyncStatus.FAILED_UNKNOWN || + calendarChannelSyncStatus === + CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS + ) { + return SyncStatus.FAILED; + } + + if ( + messageChannelSyncStatus === MessageChannelSyncStatus.ONGOING || + calendarChannelSyncStatus === CalendarChannelSyncStatus.ONGOING + ) { + return SyncStatus.IMPORTING; + } + + if ( + messageChannelSyncStatus === MessageChannelSyncStatus.NOT_SYNCED && + calendarChannelSyncStatus === CalendarChannelSyncStatus.NOT_SYNCED + ) { + return SyncStatus.NOT_SYNCED; + } + + if ( + messageChannelSyncStatus === MessageChannelSyncStatus.ACTIVE || + calendarChannelSyncStatus === CalendarChannelSyncStatus.ACTIVE + ) { + return SyncStatus.SYNCED; + } + + return SyncStatus.NOT_SYNCED; +}; diff --git a/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event.dto.ts b/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event.dto.ts index d82e1cd89..967ca31e5 100644 --- a/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event.dto.ts @@ -1,14 +1,9 @@ -import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { Field, ObjectType } from '@nestjs/graphql'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { TimelineCalendarEventParticipant } from 'src/engine/core-modules/calendar/dtos/timeline-calendar-event-participant.dto'; import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; -registerEnumType(CalendarChannelVisibility, { - name: 'CalendarChannelVisibility', - description: 'Visibility of the calendar channel', -}); - @ObjectType('LinkMetadata') class LinkMetadata { @Field() diff --git a/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts b/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts index 431f00d5a..779fadb81 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts @@ -1,14 +1,9 @@ -import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { Field, ObjectType } from '@nestjs/graphql'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { TimelineThreadParticipant } from 'src/engine/core-modules/messaging/dtos/timeline-thread-participant.dto'; import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; -registerEnumType(MessageChannelVisibility, { - name: 'MessageChannelVisibility', - description: 'Visibility of the message channel', -}); - @ObjectType('TimelineThread') export class TimelineThread { @Field(() => UUIDScalarType) diff --git a/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity.ts b/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity.ts index 25c528f99..b6bb2b803 100644 --- a/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity.ts +++ b/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity.ts @@ -1,3 +1,5 @@ +import { registerEnumType } from '@nestjs/graphql'; + import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -47,6 +49,22 @@ export enum CalendarChannelContactAutoCreationPolicy { NONE = 'NONE', } +registerEnumType(CalendarChannelVisibility, { + name: 'CalendarChannelVisibility', +}); + +registerEnumType(CalendarChannelSyncStatus, { + name: 'CalendarChannelSyncStatus', +}); + +registerEnumType(CalendarChannelSyncStage, { + name: 'CalendarChannelSyncStage', +}); + +registerEnumType(CalendarChannelContactAutoCreationPolicy, { + name: 'CalendarChannelContactAutoCreationPolicy', +}); + @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.calendarChannel, namePlural: 'calendarChannels', diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts index f5051f7c9..3b5b6e3a6 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts @@ -1,3 +1,5 @@ +import { registerEnumType } from '@nestjs/graphql'; + import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -52,6 +54,26 @@ export enum MessageChannelContactAutoCreationPolicy { NONE = 'NONE', } +registerEnumType(MessageChannelVisibility, { + name: 'MessageChannelVisibility', +}); + +registerEnumType(MessageChannelSyncStatus, { + name: 'MessageChannelSyncStatus', +}); + +registerEnumType(MessageChannelSyncStage, { + name: 'MessageChannelSyncStage', +}); + +registerEnumType(MessageChannelType, { + name: 'MessageChannelType', +}); + +registerEnumType(MessageChannelContactAutoCreationPolicy, { + name: 'MessageChannelContactAutoCreationPolicy', +}); + @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.messageChannel, namePlural: 'messageChannels',