diff --git a/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts b/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts index 91999fe7cf4..9531284adee 100644 --- a/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts @@ -9,6 +9,7 @@ import { type ExceptionHandlerOptions } from 'src/engine/core-modules/exception- import { PostgresException } from 'src/engine/api/graphql/workspace-query-runner/utils/postgres-exception'; import { type ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces'; +import { MessageImportDriverException } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { CustomException } from 'src/utils/custom-exception'; export class ExceptionHandlerSentryDriver @@ -61,6 +62,20 @@ export class ExceptionHandlerSentryDriver }); } + if ('context' in exception && exception.context) { + Object.entries(exception.context).forEach(([key, value]) => { + scope.setExtra(key, value); + }); + } + + if ('cause' in exception && exception.cause) { + scope.setContext('cause', { + name: exception.cause.name, + message: exception.cause.message, + stack: exception.cause.stack, + }); + } + if ( exception instanceof CustomException && exception.code !== 'UNKNOWN' @@ -84,6 +99,11 @@ export class ExceptionHandlerSentryDriver exception.name = exception.message; } + if (exception instanceof MessageImportDriverException) { + scope.setTag('messageImportDriverCode', exception.code); + scope.setFingerprint([exception.code]); + } + const eventId = Sentry.captureException(exception, { contexts: { GraphQL: { diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service.ts index 8abc2b952e3..9ab942b72d7 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service.ts @@ -8,7 +8,7 @@ import { import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service'; import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { MESSAGING_GMAIL_DEFAULT_NOT_SYNCED_LABELS } from 'src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-default-not-synced-labels'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service'; @Injectable() export class GmailGetAllFoldersService implements MessageFolderDriver { @@ -16,7 +16,7 @@ export class GmailGetAllFoldersService implements MessageFolderDriver { constructor( private readonly oAuth2ClientManagerService: OAuth2ClientManagerService, - private readonly gmailHandleErrorService: GmailHandleErrorService, + private readonly gmailMessageListFetchErrorHandler: GmailMessageListFetchErrorHandler, ) {} private isSyncedByDefault(labelId: string): boolean { @@ -44,7 +44,7 @@ export class GmailGetAllFoldersService implements MessageFolderDriver { `Connected account ${connectedAccount.id}: Error fetching labels: ${error.message}`, ); - this.gmailHandleErrorService.handleGmailMessageListFetchError(error); + this.gmailMessageListFetchErrorHandler.handleError(error); return { data: { labels: [] } }; }); diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service.ts index eb83e33068f..dd23c8fff75 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service.ts @@ -7,7 +7,7 @@ import { import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service'; import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; -import { MicrosoftHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service'; +import { MicrosoftMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service'; import { StandardFolder } from 'src/modules/messaging/message-import-manager/drivers/types/standard-folder'; import { getStandardFolderByRegex } from 'src/modules/messaging/message-import-manager/drivers/utils/get-standard-folder-by-regex'; @@ -26,8 +26,7 @@ export class MicrosoftGetAllFoldersService implements MessageFolderDriver { constructor( private readonly oAuth2ClientManagerService: OAuth2ClientManagerService, - - private readonly microsoftHandleErrorService: MicrosoftHandleErrorService, + private readonly microsoftMessageListFetchErrorHandler: MicrosoftMessageListFetchErrorHandler, ) {} async getAllMessageFolders( @@ -51,9 +50,7 @@ export class MicrosoftGetAllFoldersService implements MessageFolderDriver { this.logger.error( `Connected account ${connectedAccount.id}: Error fetching folders: ${error.message}`, ); - this.microsoftHandleErrorService.handleMicrosoftGetMessageListError( - error, - ); + this.microsoftMessageListFetchErrorHandler.handleError(error); return { value: [] }; }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts index ff029b95b02..2685ca6cdbe 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts @@ -1,9 +1,37 @@ -import { CustomException } from 'src/utils/custom-exception'; import { type MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception'; -export class MessageImportDriverException extends CustomException< - MessageImportDriverExceptionCode | MessageNetworkExceptionCode -> {} +export class MessageImportDriverException extends Error { + code: MessageImportDriverExceptionCode | MessageNetworkExceptionCode; + cause?: Error; + context?: { + messageChannelId?: string; + workspaceId?: string; + syncStep?: string; + }; + + constructor( + message: string, + code: MessageImportDriverExceptionCode | MessageNetworkExceptionCode, + options?: { + cause?: Error; + context?: { + messageChannelId?: string; + workspaceId?: string; + syncStep?: string; + }; + }, + ) { + super(message); + this.name = 'MessageImportDriverException'; + this.code = code; + this.cause = options?.cause; + this.context = options?.context; + + if (options?.cause?.stack) { + this.stack = `${this.stack}\nCaused by: ${options.cause.stack}`; + } + } +} export enum MessageImportDriverExceptionCode { NOT_FOUND = 'NOT_FOUND', diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module.ts index 35ed0548099..780fcb4d8c2 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module.ts @@ -15,7 +15,9 @@ import { GmailFetchByBatchService } from 'src/modules/messaging/message-import-m import { GmailGetHistoryService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service'; import { GmailGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service'; import { GmailGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service'; +import { GmailMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-messages-import-error-handler.service'; +import { GmailNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service'; import { MessageParticipantManagerModule } from 'src/modules/messaging/message-participant-manager/message-participant-manager.module'; @Module({ @@ -38,13 +40,16 @@ import { MessageParticipantManagerModule } from 'src/modules/messaging/message-p GmailFetchByBatchService, GmailGetMessagesService, GmailGetMessageListService, - GmailHandleErrorService, + GmailNetworkErrorHandler, + GmailMessageListFetchErrorHandler, + GmailMessagesImportErrorHandler, ], exports: [ GmailGetMessagesService, GmailGetMessageListService, - - GmailHandleErrorService, + GmailNetworkErrorHandler, + GmailMessageListFetchErrorHandler, + GmailMessagesImportErrorHandler, ], }) export class MessagingGmailDriverModule {} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service.ts index 46cab1bdc94..65d50ba3576 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service.ts @@ -3,12 +3,12 @@ import { Injectable } from '@nestjs/common'; import { type gmail_v1 } from 'googleapis'; import { MESSAGING_GMAIL_USERS_HISTORY_MAX_RESULT } from 'src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-users-history-max-result.constant'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service'; @Injectable() export class GmailGetHistoryService { constructor( - private readonly gmailHandleErrorService: GmailHandleErrorService, + private readonly gmailMessageListFetchErrorHandler: GmailMessageListFetchErrorHandler, ) {} public async getHistory( @@ -36,7 +36,7 @@ export class GmailGetHistoryService { labelId, }) .catch((error) => { - this.gmailHandleErrorService.handleGmailMessageListFetchError(error); + this.gmailMessageListFetchErrorHandler.handleError(error); return { data: { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.spec.ts index 579916d93c3..ce76e25cf5c 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.spec.ts @@ -6,7 +6,7 @@ import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2 import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { GmailGetHistoryService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service'; import { GmailGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service'; describe('GmailGetMessageListService', () => { let service: GmailGetMessageListService; @@ -47,10 +47,9 @@ describe('GmailGetMessageListService', () => { }, }, { - provide: GmailHandleErrorService, + provide: GmailMessageListFetchErrorHandler, useValue: { - handleGmailMessageListFetchError: jest.fn(), - handleGmailMessagesImportError: jest.fn(), + handleError: jest.fn(), }, }, ], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.ts index 2b3e031af41..6443b441a25 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service.ts @@ -13,7 +13,7 @@ import { } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { MESSAGING_GMAIL_USERS_MESSAGES_LIST_MAX_RESULT } from 'src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-users-messages-list-max-result.constant'; import { GmailGetHistoryService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-history.service'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service'; import { computeGmailExcludeSearchFilter } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/compute-gmail-exclude-search-filter.util'; import { type GetMessageListsArgs } from 'src/modules/messaging/message-import-manager/types/get-message-lists-args.type'; import { type GetMessageListsResponse } from 'src/modules/messaging/message-import-manager/types/get-message-lists-response.type'; @@ -25,8 +25,7 @@ export class GmailGetMessageListService { constructor( private readonly gmailGetHistoryService: GmailGetHistoryService, private readonly oAuth2ClientManagerService: OAuth2ClientManagerService, - - private readonly gmailHandleErrorService: GmailHandleErrorService, + private readonly gmailMessageListFetchErrorHandler: GmailMessageListFetchErrorHandler, ) {} private async getMessageListWithoutCursor( @@ -69,7 +68,7 @@ export class GmailGetMessageListService { `Connected account ${connectedAccount.id}: Error fetching message list: ${JSON.stringify(error)}`, ); - this.gmailHandleErrorService.handleGmailMessageListFetchError(error); + this.gmailMessageListFetchErrorHandler.handleError(error); return { data: { @@ -112,10 +111,7 @@ export class GmailGetMessageListService { id: firstMessageExternalId, }) .catch((error) => { - this.gmailHandleErrorService.handleGmailMessagesImportError( - error, - firstMessageExternalId as string, - ); + this.gmailMessageListFetchErrorHandler.handleError(error); }); const nextSyncCursor = firstMessageContent?.data?.historyId; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service.ts index 51eea1323d8..dc64c21a856 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service.ts @@ -6,7 +6,7 @@ import { isDefined } from 'twenty-shared/utils'; import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { GmailFetchByBatchService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-fetch-by-batch.service'; -import { GmailHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service'; +import { GmailMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-messages-import-error-handler.service'; import { parseAndFormatGmailMessage } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util'; import { type MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message'; @@ -14,7 +14,7 @@ import { type MessageWithParticipants } from 'src/modules/messaging/message-impo export class GmailGetMessagesService { constructor( private readonly fetchByBatchesService: GmailFetchByBatchService, - private readonly gmailHandleErrorService: GmailHandleErrorService, + private readonly gmailMessagesImportErrorHandler: GmailMessagesImportErrorHandler, ) {} async getMessages( @@ -57,7 +57,7 @@ export class GmailGetMessagesService { const messages = parsedResponses.map((response, index) => { if ('error' in response) { - this.gmailHandleErrorService.handleGmailMessagesImportError( + this.gmailMessagesImportErrorHandler.handleError( response.error, messageIds[index], ); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service.ts deleted file mode 100644 index 06ef6f91c15..00000000000 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-handle-error.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { - MessageImportDriverException, - MessageImportDriverExceptionCode, -} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; -import { isAxiosTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-axios-gaxios-error.util'; -import { parseGmailMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util'; -import { parseGmailMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util'; - -@Injectable() -export class GmailHandleErrorService { - constructor() {} - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public handleGmailMessageListFetchError(error: any): void { - if (isAxiosTemporaryError(error)) { - throw new MessageImportDriverException( - error.message, - MessageImportDriverExceptionCode.TEMPORARY_ERROR, - ); - } - - throw parseGmailMessageListFetchError(error); - } - - public handleGmailMessagesImportError( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - error: any, - messageExternalId: string, - ): void { - if (isAxiosTemporaryError(error)) { - throw new MessageImportDriverException( - error.message, - MessageImportDriverExceptionCode.TEMPORARY_ERROR, - ); - } - - const gmailError = parseGmailMessagesImportError(error, messageExternalId); - - if (gmailError) { - throw gmailError; - } - } -} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service.ts new file mode 100644 index 00000000000..7b71e884041 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service.ts @@ -0,0 +1,26 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { GmailNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service'; +import { parseGmailMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util'; + +@Injectable() +export class GmailMessageListFetchErrorHandler { + private readonly logger = new Logger(GmailMessageListFetchErrorHandler.name); + + constructor( + private readonly gmailNetworkErrorHandler: GmailNetworkErrorHandler, + ) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public handleError(error: any): void { + this.logger.log(`Error fetching message list`, error); + + const networkError = this.gmailNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + throw parseGmailMessageListFetchError(error, { cause: error }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-messages-import-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-messages-import-error-handler.service.ts new file mode 100644 index 00000000000..a9dd74e150a --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-messages-import-error-handler.service.ts @@ -0,0 +1,32 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { GmailNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service'; +import { parseGmailMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util'; + +@Injectable() +export class GmailMessagesImportErrorHandler { + private readonly logger = new Logger(GmailMessagesImportErrorHandler.name); + + constructor( + private readonly gmailNetworkErrorHandler: GmailNetworkErrorHandler, + ) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public handleError(error: any, messageExternalId: string): void { + this.logger.log(`Error fetching messages`, error); + + const networkError = this.gmailNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + const gmailError = parseGmailMessagesImportError(error, messageExternalId, { + cause: error, + }); + + if (gmailError) { + throw gmailError; + } + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service.ts new file mode 100644 index 00000000000..36b55d6127e --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-network-error-handler.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; + +import { + MessageImportDriverException, + MessageImportDriverExceptionCode, +} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; +import { isAxiosTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-axios-gaxios-error.util'; + +@Injectable() +export class GmailNetworkErrorHandler { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public handleError(error: any): MessageImportDriverException | null { + if (isAxiosTemporaryError(error)) { + return new MessageImportDriverException( + error.message, + MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: error }, + ); + } + + return null; + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util.ts index 61453828568..9fc3cb541c5 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util.ts @@ -3,13 +3,16 @@ import { MessageImportDriverExceptionCode, } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; -export const parseGmailMessageListFetchError = (error: { - code?: number; - errors: { - reason: string; - message: string; - }[]; -}): MessageImportDriverException => { +export const parseGmailMessageListFetchError = ( + error: { + code?: number; + errors: { + reason: string; + message: string; + }[]; + }, + options?: { cause?: Error }, +): MessageImportDriverException => { const { code, errors } = error; const reason = errors?.[0]?.reason; @@ -21,6 +24,7 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } if (reason === 'failedPrecondition') { @@ -28,30 +32,35 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } return new MessageImportDriverException( message, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); case 404: return new MessageImportDriverException( message, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause }, ); case 429: return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); case 403: @@ -63,12 +72,14 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } if (reason === 'domainPolicy') { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } @@ -78,12 +89,14 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); case 503: return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); case 500: @@ -93,6 +106,7 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } @@ -100,6 +114,7 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( `${code} - ${reason} - ${message}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } break; @@ -111,5 +126,6 @@ export const parseGmailMessageListFetchError = (error: { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util.ts index e4ed11dac35..0058e0ee767 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util.ts @@ -12,6 +12,7 @@ export const parseGmailMessagesImportError = ( }[]; }, messageExternalId: string, + options?: { cause?: Error }, ): MessageImportDriverException | undefined => { const { code, errors } = error; @@ -25,6 +26,7 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } if (reason === 'failedPrecondition') { @@ -32,18 +34,21 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } return new MessageImportDriverException( message, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); case 404: @@ -54,6 +59,7 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); case 403: @@ -65,12 +71,14 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } if (reason === 'domainPolicy') { return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } @@ -80,12 +88,14 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); case 503: return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); case 500: @@ -95,6 +105,7 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } @@ -102,6 +113,7 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( `${code} - ${reason} - ${message}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } break; @@ -113,5 +125,6 @@ export const parseGmailMessagesImportError = ( return new MessageImportDriverException( message, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module.ts index 1599df050ec..690019593a9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module.ts @@ -14,11 +14,13 @@ import { ImapFetchByBatchService } from 'src/modules/messaging/message-import-ma import { ImapFindSentFolderService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-find-sent-folder.service'; import { ImapGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service'; import { ImapGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service'; -import { ImapHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service'; import { ImapIncrementalSyncService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-incremental-sync.service'; import { ImapMessageFetcherService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-fetcher.service'; +import { ImapMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-list-fetch-error-handler.service'; import { ImapMessageProcessorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-processor.service'; +import { ImapMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-messages-import-error-handler.service'; import { ImapMessageTextExtractorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-text-extractor.service'; +import { ImapNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service'; import { MessageParticipantManagerModule } from 'src/modules/messaging/message-participant-manager/message-participant-manager.module'; @Module({ @@ -37,7 +39,9 @@ import { MessageParticipantManagerModule } from 'src/modules/messaging/message-p ImapFetchByBatchService, ImapGetMessagesService, ImapGetMessageListService, - ImapHandleErrorService, + ImapNetworkErrorHandler, + ImapMessageListFetchErrorHandler, + ImapMessagesImportErrorHandler, ImapIncrementalSyncService, ImapMessageFetcherService, ImapMessageProcessorService, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service.ts index 288834d2884..ae1b84990e1 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service.ts @@ -4,7 +4,7 @@ import { type ImapFlow } from 'imapflow'; import { type MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity'; import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider'; -import { ImapHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service'; +import { ImapMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-list-fetch-error-handler.service'; import { ImapIncrementalSyncService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-incremental-sync.service'; import { createSyncCursor } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/create-sync-cursor.util'; import { extractMailboxState } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/extract-mailbox-state.util'; @@ -25,7 +25,7 @@ export class ImapGetMessageListService { constructor( private readonly imapClientProvider: ImapClientProvider, private readonly imapIncrementalSyncService: ImapIncrementalSyncService, - private readonly imapHandleErrorService: ImapHandleErrorService, + private readonly imapMessageListFetchErrorHandler: ImapMessageListFetchErrorHandler, ) {} public async getMessageLists({ @@ -74,7 +74,7 @@ export class ImapGetMessageListService { error.stack, ); - this.imapHandleErrorService.handleImapMessageListFetchError(error); + this.imapMessageListFetchErrorHandler.handleError(error); return messageFolders.map((folder) => ({ messageExternalIds: [], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service.ts deleted file mode 100644 index 43c1d5fc434..00000000000 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; - -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { - MessageChannelSyncStatus, - type MessageChannelWorkspaceEntity, -} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; -import { parseImapError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util'; -import { parseImapMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util'; -import { parseImapMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util'; - -@Injectable() -export class ImapHandleErrorService { - private readonly logger = new Logger(ImapHandleErrorService.name); - - constructor( - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - ) {} - - async handleError( - error: Error, - workspaceId: string, - messageChannelId: string, - ): Promise { - this.logger.error( - `IMAP error for message channel ${messageChannelId}: ${error.message}`, - error.stack, - ); - - try { - const messageChannelRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'messageChannel', - ); - - await messageChannelRepository.update( - { id: messageChannelId }, - { - syncStatus: MessageChannelSyncStatus.FAILED_UNKNOWN, - }, - ); - } catch (handleErrorError) { - this.logger.error( - `Error handling IMAP error: ${handleErrorError.message}`, - handleErrorError.stack, - ); - } - } - - public handleImapMessageListFetchError(error: Error): void { - const imapError = parseImapError(error); - - if (imapError) { - throw imapError; - } - - throw parseImapMessageListFetchError(error); - } - - public handleImapMessagesImportError( - error: Error, - messageExternalId: string, - ): void { - const imapError = parseImapError(error); - - if (imapError) { - throw imapError; - } - - throw parseImapMessagesImportError(error, messageExternalId); - } -} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-list-fetch-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-list-fetch-error-handler.service.ts new file mode 100644 index 00000000000..7fbeab41514 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-list-fetch-error-handler.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; + +import { parseImapMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util'; +import { ImapNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service'; + +@Injectable() +export class ImapMessageListFetchErrorHandler { + constructor( + private readonly imapNetworkErrorHandler: ImapNetworkErrorHandler, + ) {} + + public handleError(error: Error): void { + const networkError = this.imapNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + throw parseImapMessageListFetchError(error, { cause: error }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-processor.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-processor.service.ts index 62164fd8d96..2a386e9fcc9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-processor.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-processor.service.ts @@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { type FetchMessageObject, type ImapFlow } from 'imapflow'; import { type ParsedMail, simpleParser } from 'mailparser'; -import { ImapHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-handle-error.service'; +import { ImapMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-messages-import-error-handler.service'; export type MessageFetchResult = { uid: number; @@ -16,7 +16,7 @@ export class ImapMessageProcessorService { private readonly logger = new Logger(ImapMessageProcessorService.name); constructor( - private readonly imapHandleErrorService: ImapHandleErrorService, + private readonly imapMessagesImportErrorHandler: ImapMessagesImportErrorHandler, ) {} async processMessagesByUidsInFolder( @@ -174,7 +174,7 @@ export class ImapMessageProcessorService { return uids.map((uid) => { this.logger.error(`Failed to fetch message UID ${uid}: ${error.message}`); - this.imapHandleErrorService.handleImapMessagesImportError( + this.imapMessagesImportErrorHandler.handleError( error, `${folder}:${uid}`, ); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-messages-import-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-messages-import-error-handler.service.ts new file mode 100644 index 00000000000..73a16215802 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-messages-import-error-handler.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; + +import { parseImapMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util'; +import { ImapNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service'; + +@Injectable() +export class ImapMessagesImportErrorHandler { + constructor( + private readonly imapNetworkErrorHandler: ImapNetworkErrorHandler, + ) {} + + public handleError(error: Error, messageExternalId: string): void { + const networkError = this.imapNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + throw parseImapMessagesImportError(error, messageExternalId, { + cause: error, + }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service.ts new file mode 100644 index 00000000000..6ca527b34ea --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-network-error-handler.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; + +import { type MessageImportDriverException } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; +import { parseImapError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util'; + +@Injectable() +export class ImapNetworkErrorHandler { + public handleError(error: Error): MessageImportDriverException | null { + return parseImapError(error, { cause: error }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util.ts index b50deb68351..97e278e0258 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-error.util.ts @@ -7,6 +7,7 @@ import { isImapFlowError } from 'src/modules/messaging/message-import-manager/dr export const parseImapError = ( error: Error, + options?: { cause?: Error }, ): MessageImportDriverException | null => { if (!error) { return null; @@ -20,6 +21,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP client not available: ${error.message}`, MessageImportDriverExceptionCode.CLIENT_NOT_AVAILABLE, + { cause: options?.cause || error }, ); } @@ -27,6 +29,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP connection timeout: ${error.message}`, MessageNetworkExceptionCode.ETIMEDOUT, + { cause: options?.cause || error }, ); } @@ -34,6 +37,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP connection error: ${error.message}`, MessageImportDriverExceptionCode.UNKNOWN_NETWORK_ERROR, + { cause: options?.cause || error }, ); } @@ -42,6 +46,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP authentication error: ${error.responseText || error.message}`, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause || error }, ); } @@ -49,6 +54,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP mailbox not found: ${error.responseText || error.message}`, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause || error }, ); } } @@ -57,6 +63,7 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP authentication error: ${error.responseText || error.message}`, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause || error }, ); } @@ -65,12 +72,14 @@ export const parseImapError = ( return new MessageImportDriverException( `IMAP temporary error: ${error.responseText}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause || error }, ); } return new MessageImportDriverException( `IMAP command failed: ${error.responseText}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause || error }, ); } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util.ts index 99896db3a81..cc771e2f2bf 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-message-list-fetch-error.util.ts @@ -6,11 +6,13 @@ import { isImapFlowError } from 'src/modules/messaging/message-import-manager/dr export const parseImapMessageListFetchError = ( error: Error, + options?: { cause?: Error }, ): MessageImportDriverException => { if (!error) { return new MessageImportDriverException( 'Unknown IMAP message list fetch error: No error provided', MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); } @@ -20,6 +22,7 @@ export const parseImapMessageListFetchError = ( return new MessageImportDriverException( `Unknown IMAP message list fetch error: ${errorMessage}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause || error }, ); } @@ -31,6 +34,7 @@ export const parseImapMessageListFetchError = ( return new MessageImportDriverException( `IMAP sync cursor error: ${error.responseText}`, MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR, + { cause: options?.cause || error }, ); } @@ -38,6 +42,7 @@ export const parseImapMessageListFetchError = ( return new MessageImportDriverException( 'No messages found for next sync cursor', MessageImportDriverExceptionCode.NO_NEXT_SYNC_CURSOR, + { cause: options?.cause || error }, ); } } @@ -46,6 +51,7 @@ export const parseImapMessageListFetchError = ( return new MessageImportDriverException( `IMAP sync cursor error: ${errorMessage}`, MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR, + { cause: options?.cause || error }, ); } @@ -53,11 +59,13 @@ export const parseImapMessageListFetchError = ( return new MessageImportDriverException( 'No messages found for next sync cursor', MessageImportDriverExceptionCode.NO_NEXT_SYNC_CURSOR, + { cause: options?.cause || error }, ); } return new MessageImportDriverException( `Unknown IMAP message list fetch error: code: ${error.code} | responseText: ${error.responseText} | executedCommand: ${error.executedCommand}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause || error }, ); }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util.ts index 182293e9701..43e53f250ec 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-messages-import-error.util.ts @@ -7,11 +7,13 @@ import { isImapFlowError } from 'src/modules/messaging/message-import-manager/dr export const parseImapMessagesImportError = ( error: Error, messageExternalId: string, + options?: { cause?: Error }, ): MessageImportDriverException => { if (!error) { return new MessageImportDriverException( `Unknown IMAP message import error for message ${messageExternalId}: No error provided`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); } @@ -21,6 +23,7 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `Unknown IMAP message import error for message ${messageExternalId}: ${errorMessage}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause || error }, ); } @@ -29,6 +32,7 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `IMAP message not found: ${messageExternalId}`, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause || error }, ); } @@ -36,6 +40,7 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `IMAP message no longer exists (expunged): ${messageExternalId}`, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause || error }, ); } @@ -43,6 +48,7 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `IMAP message fetch error for message ${messageExternalId}: ${error.responseText}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause || error }, ); } } @@ -54,6 +60,7 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `IMAP message not found: ${messageExternalId}`, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause || error }, ); } @@ -61,11 +68,13 @@ export const parseImapMessagesImportError = ( return new MessageImportDriverException( `IMAP message fetch error for message ${messageExternalId}: ${errorMessage}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause || error }, ); } return new MessageImportDriverException( `Unknown IMAP message import error for message ${messageExternalId}: ${errorMessage}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause || error }, ); }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/messaging-microsoft-driver.module.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/messaging-microsoft-driver.module.ts index 41363252a97..62b15077d54 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/messaging-microsoft-driver.module.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/messaging-microsoft-driver.module.ts @@ -8,7 +8,9 @@ import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2- import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module'; import { MicrosoftFetchByBatchService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-fetch-by-batch.service'; import { MicrosoftGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service'; -import { MicrosoftHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service'; +import { MicrosoftMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service'; +import { MicrosoftMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-messages-import-error-handler.service'; +import { MicrosoftNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service'; import { MicrosoftGetMessageListService } from './services/microsoft-get-message-list.service'; @@ -25,12 +27,15 @@ import { MicrosoftGetMessageListService } from './services/microsoft-get-message MicrosoftGetMessageListService, MicrosoftGetMessagesService, MicrosoftFetchByBatchService, - MicrosoftHandleErrorService, + MicrosoftNetworkErrorHandler, + MicrosoftMessageListFetchErrorHandler, + MicrosoftMessagesImportErrorHandler, ], exports: [ MicrosoftGetMessageListService, MicrosoftGetMessagesService, - MicrosoftHandleErrorService, + MicrosoftMessageListFetchErrorHandler, + MicrosoftMessagesImportErrorHandler, ], }) export class MessagingMicrosoftDriverModule {} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.dev.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.dev.spec.ts index 120fed27c56..d2a72c7618d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.dev.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.dev.spec.ts @@ -13,7 +13,7 @@ import { microsoftGraphWithMessagesDeltaLink } from 'src/modules/messaging/messa import { MessageFolderName } from 'src/modules/messaging/message-import-manager/drivers/microsoft/types/folders'; import { MicrosoftGetMessageListService } from './microsoft-get-message-list.service'; -import { MicrosoftHandleErrorService } from './microsoft-handle-error.service'; +import { MicrosoftMessageListFetchErrorHandler } from './microsoft-message-list-fetch-error-handler.service'; // in case you have "Please provide a valid token" it may be because you need to pass the env varible to the .env.test file const accessToken = 'replace-with-your-access-token'; @@ -53,7 +53,10 @@ xdescribe('Microsoft dev tests : get message list service', () => { providers: [ MicrosoftGetMessageListService, OAuth2ClientManagerService, - MicrosoftHandleErrorService, + { + provide: MicrosoftMessageListFetchErrorHandler, + useValue: { handleError: jest.fn() }, + }, MicrosoftOAuth2ClientManagerService, ConfigService, ], @@ -204,7 +207,10 @@ xdescribe('Microsoft dev tests : get message list service for folders', () => { providers: [ MicrosoftGetMessageListService, OAuth2ClientManagerService, - MicrosoftHandleErrorService, + { + provide: MicrosoftMessageListFetchErrorHandler, + useValue: { handleError: jest.fn() }, + }, MicrosoftOAuth2ClientManagerService, ConfigService, ], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.ts index 37589ca8070..9d680ae432d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service.ts @@ -14,7 +14,7 @@ import { MessageImportDriverException, MessageImportDriverExceptionCode, } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; -import { MicrosoftHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service'; +import { MicrosoftMessageListFetchErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service'; import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils'; import { type GetMessageListsArgs } from 'src/modules/messaging/message-import-manager/types/get-message-lists-args.type'; import { @@ -30,7 +30,7 @@ export class MicrosoftGetMessageListService { private readonly logger = new Logger(MicrosoftGetMessageListService.name); constructor( private readonly oAuth2ClientManagerService: OAuth2ClientManagerService, - private readonly microsoftHandleErrorService: MicrosoftHandleErrorService, + private readonly microsoftMessageListFetchErrorHandler: MicrosoftMessageListFetchErrorHandler, ) {} public async getMessageLists({ @@ -99,9 +99,7 @@ export class MicrosoftGetMessageListService { MessageImportDriverExceptionCode.CLIENT_NOT_AVAILABLE, ); } - this.microsoftHandleErrorService.handleMicrosoftGetMessageListError( - error, - ); + this.microsoftMessageListFetchErrorHandler.handleError(error); }); const callback: PageIteratorCallback = (data) => { @@ -127,9 +125,7 @@ export class MicrosoftGetMessageListService { MessageImportDriverExceptionCode.CLIENT_NOT_AVAILABLE, ); } - this.microsoftHandleErrorService.handleMicrosoftGetMessageListError( - error, - ); + this.microsoftMessageListFetchErrorHandler.handleError(error); }); return { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.dev.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.dev.spec.ts index 1928f1ba012..e30c33e0635 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.dev.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.dev.spec.ts @@ -9,7 +9,7 @@ import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2 import { MicrosoftFetchByBatchService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-fetch-by-batch.service'; import { MicrosoftGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service'; -import { MicrosoftHandleErrorService } from './microsoft-handle-error.service'; +import { MicrosoftMessagesImportErrorHandler } from './microsoft-messages-import-error-handler.service'; const mockMessageIds = [ 'AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AGnUPtcQC-Eiwmc39SmMpPgAAA8ZAfgAA', @@ -27,7 +27,10 @@ xdescribe('Microsoft dev tests : get messages service', () => { imports: [TwentyConfigModule.forRoot()], providers: [ MicrosoftGetMessagesService, - MicrosoftHandleErrorService, + { + provide: MicrosoftMessagesImportErrorHandler, + useValue: { handleError: jest.fn() }, + }, OAuth2ClientManagerService, MicrosoftOAuth2ClientManagerService, MicrosoftFetchByBatchService, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts index c200d59deee..1ec09e6f025 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts @@ -1,5 +1,5 @@ -import { ConfigService } from '@nestjs/config'; import { Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { Test, type TestingModule } from '@nestjs/testing'; import { ConnectedAccountProvider } from 'twenty-shared/types'; @@ -15,8 +15,7 @@ import { import { MicrosoftFetchByBatchService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-fetch-by-batch.service'; import { type MicrosoftGraphBatchResponse } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.interface'; import { MicrosoftGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service'; - -import { MicrosoftHandleErrorService } from './microsoft-handle-error.service'; +import { MicrosoftMessagesImportErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-messages-import-error-handler.service'; describe('Microsoft get messages service', () => { let service: MicrosoftGetMessagesService; @@ -25,7 +24,10 @@ describe('Microsoft get messages service', () => { const module: TestingModule = await Test.createTestingModule({ providers: [ MicrosoftGetMessagesService, - MicrosoftHandleErrorService, + { + provide: MicrosoftMessagesImportErrorHandler, + useValue: { handleError: jest.fn() }, + }, OAuth2ClientManagerService, GoogleOAuth2ClientManagerService, MicrosoftOAuth2ClientManagerService, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts index cfe5f36264c..cf9b4e016a9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts @@ -13,7 +13,7 @@ import { formatAddressObjectAsParticipants } from 'src/modules/messaging/message import { safeParseEmailAddress } from 'src/modules/messaging/message-import-manager/utils/safe-parse.util'; import { MicrosoftFetchByBatchService } from './microsoft-fetch-by-batch.service'; -import { MicrosoftHandleErrorService } from './microsoft-handle-error.service'; +import { MicrosoftMessagesImportErrorHandler } from './microsoft-messages-import-error-handler.service'; type ConnectedAccountType = Pick< ConnectedAccountWorkspaceEntity, @@ -31,7 +31,7 @@ export class MicrosoftGetMessagesService { constructor( private readonly microsoftFetchByBatchService: MicrosoftFetchByBatchService, - private readonly microsoftHandleErrorService: MicrosoftHandleErrorService, + private readonly microsoftMessagesImportErrorHandler: MicrosoftMessagesImportErrorHandler, ) {} async getMessages( @@ -52,7 +52,7 @@ export class MicrosoftGetMessagesService { return messages; } catch (error) { - this.microsoftHandleErrorService.handleMicrosoftGetMessagesError(error); + this.microsoftMessagesImportErrorHandler.handleError(error); return []; } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service.ts new file mode 100644 index 00000000000..d88185937ca --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-message-list-fetch-error-handler.service.ts @@ -0,0 +1,28 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { MicrosoftNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service'; +import { parseMicrosoftMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util'; + +@Injectable() +export class MicrosoftMessageListFetchErrorHandler { + private readonly logger = new Logger( + MicrosoftMessageListFetchErrorHandler.name, + ); + + constructor( + private readonly microsoftNetworkErrorHandler: MicrosoftNetworkErrorHandler, + ) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public handleError(error: any): void { + this.logger.log(`Error fetching message list`, error); + + const networkError = this.microsoftNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + throw parseMicrosoftMessagesImportError(error, { cause: error }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-messages-import-error-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-messages-import-error-handler.service.ts new file mode 100644 index 00000000000..3e7c7db5d9e --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-messages-import-error-handler.service.ts @@ -0,0 +1,28 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { MicrosoftNetworkErrorHandler } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service'; +import { parseMicrosoftMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util'; + +@Injectable() +export class MicrosoftMessagesImportErrorHandler { + private readonly logger = new Logger( + MicrosoftMessagesImportErrorHandler.name, + ); + + constructor( + private readonly microsoftNetworkErrorHandler: MicrosoftNetworkErrorHandler, + ) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public handleError(error: any): void { + this.logger.log(`Error fetching messages`, error); + + const networkError = this.microsoftNetworkErrorHandler.handleError(error); + + if (networkError) { + throw networkError; + } + + throw parseMicrosoftMessagesImportError(error, { cause: error }); + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service.ts similarity index 58% rename from packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service.ts rename to packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service.ts index 68bbf813ed6..302266d193d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-network-error-handler.service.ts @@ -6,25 +6,18 @@ import { } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils'; import { isMicrosoftClientTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-temporary-error.utils'; -import { parseMicrosoftMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util'; @Injectable() -export class MicrosoftHandleErrorService { - private readonly logger = new Logger(MicrosoftHandleErrorService.name); +export class MicrosoftNetworkErrorHandler { + private readonly logger = new Logger(MicrosoftNetworkErrorHandler.name); // eslint-disable-next-line @typescript-eslint/no-explicit-any - public handleMicrosoftGetMessageListError(error: any): void { - this.logger.log(`Error fetching message list`, error); - throw parseMicrosoftMessagesImportError(error); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public handleMicrosoftGetMessagesError(error: any): void { - this.logger.log(`Error fetching messages`, error); + public handleError(error: any): MessageImportDriverException | null { if (isAccessTokenRefreshingError(error?.body)) { - throw new MessageImportDriverException( + return new MessageImportDriverException( error.message, MessageImportDriverExceptionCode.CLIENT_NOT_AVAILABLE, + { cause: error }, ); } @@ -33,12 +26,13 @@ export class MicrosoftHandleErrorService { isBodyString && isMicrosoftClientTemporaryError(error.body); if (isTemporaryError) { - throw new MessageImportDriverException( + return new MessageImportDriverException( `code: ${error.code} - body: ${error.body}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: error }, ); } - throw parseMicrosoftMessagesImportError(error); + return null; } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util.ts index a27b377be56..405e1957d40 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/utils/parse-microsoft-messages-import.util.ts @@ -3,15 +3,19 @@ import { MessageImportDriverExceptionCode, } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; -export const parseMicrosoftMessagesImportError = (error: { - statusCode: number; - message?: string; - code?: string; -}): MessageImportDriverException => { +export const parseMicrosoftMessagesImportError = ( + error: { + statusCode: number; + message?: string; + code?: string; + }, + options?: { cause?: Error }, +): MessageImportDriverException => { if (error.statusCode === 401) { return new MessageImportDriverException( 'Unauthorized access to Microsoft Graph API', MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } @@ -19,6 +23,7 @@ export const parseMicrosoftMessagesImportError = (error: { return new MessageImportDriverException( 'Forbidden access to Microsoft Graph API', MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } @@ -31,11 +36,13 @@ export const parseMicrosoftMessagesImportError = (error: { return new MessageImportDriverException( `Disabled, deleted, inactive or no licence Microsoft account - code:${error.code}`, MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS, + { cause: options?.cause }, ); } else { return new MessageImportDriverException( `Not found - code:${error.code}`, MessageImportDriverExceptionCode.NOT_FOUND, + { cause: options?.cause }, ); } } @@ -48,11 +55,13 @@ export const parseMicrosoftMessagesImportError = (error: { return new MessageImportDriverException( `Microsoft Graph API ${error.code} ${error.statusCode} error: ${error.message}`, MessageImportDriverExceptionCode.TEMPORARY_ERROR, + { cause: options?.cause }, ); } return new MessageImportDriverException( `Microsoft Graph API unknown error: ${error} with status code ${error.statusCode}`, MessageImportDriverExceptionCode.UNKNOWN, + { cause: options?.cause }, ); }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/exceptions/message-import.exception.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/exceptions/message-import.exception.ts deleted file mode 100644 index 31de71dbce1..00000000000 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/exceptions/message-import.exception.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { CustomException } from 'src/utils/custom-exception'; - -export class MessageImportException extends CustomException {} - -export enum MessageImportExceptionCode { - UNKNOWN = 'UNKNOWN', - PROVIDER_NOT_SUPPORTED = 'PROVIDER_NOT_SUPPORTED', -} diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-message-list.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-message-list.service.ts index fa810bc4d65..e4b01a0582b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-message-list.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-message-list.service.ts @@ -7,13 +7,13 @@ import { type MessageChannelWorkspaceEntity, } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; import { MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity'; +import { + MessageImportDriverException, + MessageImportDriverExceptionCode, +} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { GmailGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service'; import { ImapGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service'; import { MicrosoftGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service'; -import { - MessageImportException, - MessageImportExceptionCode, -} from 'src/modules/messaging/message-import-manager/exceptions/message-import.exception'; import { type GetMessageListsResponse } from 'src/modules/messaging/message-import-manager/types/get-message-lists-response.type'; type MessageFolder = Pick< @@ -60,9 +60,9 @@ export class MessagingGetMessageListService { }); } default: - throw new MessageImportException( + throw new MessageImportDriverException( `Provider ${messageChannel.connectedAccount.provider} is not supported`, - MessageImportExceptionCode.PROVIDER_NOT_SUPPORTED, + MessageImportDriverExceptionCode.PROVIDER_NOT_SUPPORTED, ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-messages.service.ts index 97e584b34e1..e38639ac24a 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-get-messages.service.ts @@ -3,13 +3,13 @@ import { Injectable } from '@nestjs/common'; import { ConnectedAccountProvider } from 'twenty-shared/types'; import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; +import { + MessageImportDriverException, + MessageImportDriverExceptionCode, +} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { GmailGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.service'; import { ImapGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service'; import { MicrosoftGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service'; -import { - MessageImportException, - MessageImportExceptionCode, -} from 'src/modules/messaging/message-import-manager/exceptions/message-import.exception'; import { type MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message'; export type GetMessagesResponse = MessageWithParticipants[]; @@ -53,9 +53,9 @@ export class MessagingGetMessagesService { connectedAccount, ); default: - throw new MessageImportException( + throw new MessageImportDriverException( `Provider ${connectedAccount.provider} is not supported`, - MessageImportExceptionCode.PROVIDER_NOT_SUPPORTED, + MessageImportDriverExceptionCode.PROVIDER_NOT_SUPPORTED, ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts index 736ed317ef4..15355859b9b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { isDefined } from 'class-validator'; - import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service'; import { type TwentyORMException, @@ -19,10 +17,6 @@ import { MessageImportDriverExceptionCode, } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; import { MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception'; -import { - MessageImportException, - MessageImportExceptionCode, -} from 'src/modules/messaging/message-import-manager/exceptions/message-import.exception'; export enum MessageImportSyncStep { MESSAGE_LIST_FETCH = 'MESSAGE_LIST_FETCH', @@ -47,6 +41,19 @@ export class MessageImportExceptionHandlerService { >, workspaceId: string, ): Promise { + if (exception instanceof MessageImportDriverException) { + exception.context = { + ...exception.context, + messageChannelId: messageChannel.id, + workspaceId, + syncStep, + }; + } + + this.exceptionHandlerService.captureExceptions([exception], { + workspace: { id: workspaceId }, + }); + if ('code' in exception) { switch (exception.code) { case MessageImportDriverExceptionCode.NOT_FOUND: @@ -78,24 +85,16 @@ export class MessageImportExceptionHandlerService { ); break; case MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR: - await this.handlePermanentException( - exception, - messageChannel, - workspaceId, - ); + await this.handlePermanentException(messageChannel, workspaceId); break; case MessageImportDriverExceptionCode.UNKNOWN: case MessageImportDriverExceptionCode.UNKNOWN_NETWORK_ERROR: default: - await this.handleUnknownException( - exception, - messageChannel, - workspaceId, - ); + await this.handleUnknownException(messageChannel, workspaceId); break; } } else { - await this.handleUnknownException(exception, messageChannel, workspaceId); + await this.handleUnknownException(messageChannel, workspaceId); } } @@ -119,7 +118,9 @@ export class MessageImportExceptionHandlerService { this.exceptionHandlerService.captureExceptions( [ - `Temporary error occurred ${MESSAGING_THROTTLE_MAX_ATTEMPTS} times while importing messages for message channel ${messageChannel.id.slice(0, 5)}... in workspace ${workspaceId}: ${exception?.message}`, + new Error( + `Temporary error occurred ${MESSAGING_THROTTLE_MAX_ATTEMPTS} times while importing messages for message channel ${messageChannel.id.slice(0, 5)}... in workspace ${workspaceId}: ${exception?.message}`, + ), ], { additionalData: { @@ -129,10 +130,7 @@ export class MessageImportExceptionHandlerService { }, ); - throw new MessageImportException( - `Temporary error occurred multiple times while importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception?.message}`, - MessageImportExceptionCode.UNKNOWN, - ); + return; } const messageChannelRepository = @@ -179,7 +177,6 @@ export class MessageImportExceptionHandlerService { } private async handleUnknownException( - exception: MessageImportDriverException | Error, messageChannel: Pick, workspaceId: string, ): Promise { @@ -188,27 +185,9 @@ export class MessageImportExceptionHandlerService { workspaceId, MessageChannelSyncStatus.FAILED_UNKNOWN, ); - - const messageImportException = new MessageImportException( - isDefined(exception.name) - ? `${exception.name}: ${exception.message}` - : exception.message, - MessageImportExceptionCode.UNKNOWN, - ); - - this.exceptionHandlerService.captureExceptions([messageImportException], { - additionalData: { - exception, - messageChannelId: messageChannel.id, - }, - workspace: { id: workspaceId }, - }); - - throw messageImportException; } private async handlePermanentException( - exception: MessageImportDriverException, messageChannel: Pick, workspaceId: string, ): Promise { @@ -217,11 +196,6 @@ export class MessageImportExceptionHandlerService { workspaceId, MessageChannelSyncStatus.FAILED_UNKNOWN, ); - - throw new MessageImportException( - `Permanent error occurred while importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception.message}`, - MessageImportExceptionCode.UNKNOWN, - ); } private async handleNotFoundException( @@ -230,14 +204,27 @@ export class MessageImportExceptionHandlerService { workspaceId: string, ): Promise { if (syncStep === MessageImportSyncStep.MESSAGE_LIST_FETCH) { - await this.handleUnknownException( - new MessageImportDriverException( - 'Not Found exception occurred while fetching message list, which should never happen', - MessageImportDriverExceptionCode.UNKNOWN, - ), - messageChannel, + await this.messageChannelSyncStatusService.markAsFailed( + [messageChannel.id], workspaceId, + MessageChannelSyncStatus.FAILED_UNKNOWN, ); + + this.exceptionHandlerService.captureExceptions( + [ + new Error( + 'Not Found exception occurred while fetching message list, which should never happen', + ), + ], + { + additionalData: { + messageChannelId: messageChannel.id, + }, + workspace: { id: workspaceId }, + }, + ); + + return; } await this.messageChannelSyncStatusService.resetAndScheduleMessageListFetch(