Refactor error messages messaging (#16094)

We should try catch locally gmail errors when:
- fetching message list
- fetching messages
- refreshing aliases
- fetching folders
This commit is contained in:
Charles Bochet
2025-11-26 18:03:44 +01:00
committed by GitHub
parent fc4dbb80de
commit 5202e2b2db
32 changed files with 543 additions and 399 deletions
@@ -0,0 +1,33 @@
import { Injectable, Logger } from '@nestjs/common';
import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@Injectable()
export class GmailEmailAliasErrorHandlerService {
private readonly logger = new Logger(GmailEmailAliasErrorHandlerService.name);
constructor() {}
public handleError(error: unknown): void {
this.logger.error(`Google: Error getting email aliases: ${error}`);
if (isGmailNetworkError(error)) {
throw parseGmailNetworkError(error);
}
if (isGmailApiError(error)) {
throw parseGmailApiError(error);
}
throw new MessageImportDriverException(
'Unknown error',
MessageImportDriverExceptionCode.UNKNOWN,
);
}
}
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { google } from 'googleapis';
import { GmailEmailAliasErrorHandlerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-error-handler.service';
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';
@@ -9,6 +10,7 @@ import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-acco
export class GoogleEmailAliasManagerService {
constructor(
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
private readonly gmailEmailAliasErrorHandlerService: GmailEmailAliasErrorHandlerService,
) {}
public async getHandleAliases(
@@ -24,10 +26,14 @@ export class GoogleEmailAliasManagerService {
auth: oAuth2Client,
});
const emailsResponse = await peopleClient.people.get({
resourceName: 'people/me',
personFields: 'emailAddresses',
});
const emailsResponse = await peopleClient.people
.get({
resourceName: 'people/me',
personFields: 'emailAddresses',
})
.catch((error) => {
throw this.gmailEmailAliasErrorHandlerService.handleError(error);
});
const emailAddresses = emailsResponse.data.emailAddresses;
@@ -1,7 +1,8 @@
import { Module } from '@nestjs/common';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/microsoft-email-alias-manager.service';
import { GmailEmailAliasErrorHandlerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-error-handler.service';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
@@ -10,6 +11,7 @@ import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-
providers: [
EmailAliasManagerService,
GoogleEmailAliasManagerService,
GmailEmailAliasErrorHandlerService,
MicrosoftEmailAliasManagerService,
],
exports: [EmailAliasManagerService],
@@ -4,9 +4,9 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
import { type Repository } from 'typeorm';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/microsoft-email-alias-manager.service';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
import { microsoftGraphMeResponseWithProxyAddresses } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/mocks/microsoft-api-examples';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
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';
@@ -4,8 +4,8 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
import { assertUnreachable } from 'twenty-shared/utils';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/microsoft-email-alias-manager.service';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@Injectable()
@@ -11,7 +11,7 @@ import {
ConnectedAccountRefreshAccessTokenExceptionCode,
} from 'src/modules/connected-account/refresh-tokens-manager/exceptions/connected-account-refresh-tokens.exception';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { isAxiosTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-axios-gaxios-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
export type ConnectedAccountTokens = {
accessToken: string;
@@ -142,7 +142,7 @@ export class ConnectedAccountRefreshTokensService {
);
}
} catch (error) {
if (isAxiosTemporaryError(error)) {
if (isGmailNetworkError(error)) {
throw new ConnectedAccountRefreshAccessTokenException(
`Error refreshing tokens for connected account ${connectedAccount.id.slice(0, 7)} in workspace ${workspaceId.slice(0, 7)}: ${error.code}`,
ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR,
@@ -0,0 +1,33 @@
import { Injectable, Logger } from '@nestjs/common';
import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@Injectable()
export class GmailFoldersErrorHandlerService {
private readonly logger = new Logger(GmailFoldersErrorHandlerService.name);
constructor() {}
public handleError(error: unknown): void {
this.logger.error(`Gmail: Error fetching folders: ${error}`);
if (isGmailNetworkError(error)) {
throw parseGmailNetworkError(error);
}
if (isGmailApiError(error)) {
throw parseGmailApiError(error);
}
throw new MessageImportDriverException(
'Unknown error',
MessageImportDriverExceptionCode.UNKNOWN,
);
}
}
@@ -10,11 +10,11 @@ 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 { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { GmailFoldersErrorHandlerService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/services/gmail-folders-error-handler.service';
import { extractGmailFolderName } from 'src/modules/messaging/message-folder-manager/drivers/gmail/utils/extract-gmail-folder-name.util';
import { getGmailFolderParentId } from 'src/modules/messaging/message-folder-manager/drivers/gmail/utils/get-gmail-folder-parent-id.util';
import { shouldSyncFolderByDefault } from 'src/modules/messaging/message-folder-manager/utils/should-sync-folder-by-default.util';
import { MESSAGING_GMAIL_DEFAULT_NOT_SYNCED_LABELS } from 'src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-default-not-synced-labels';
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 {
@@ -22,7 +22,7 @@ export class GmailGetAllFoldersService implements MessageFolderDriver {
constructor(
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
private readonly gmailMessageListFetchErrorHandler: GmailMessageListFetchErrorHandler,
private readonly gmailFoldersErrorHandlerService: GmailFoldersErrorHandlerService,
) {}
async getAllMessageFolders(
@@ -53,7 +53,7 @@ export class GmailGetAllFoldersService implements MessageFolderDriver {
`Connected account ${connectedAccount.id}: Error fetching labels: ${error.message}`,
);
this.gmailMessageListFetchErrorHandler.handleError(error);
this.gmailFoldersErrorHandlerService.handleError(error);
return { data: { labels: [] } };
});
@@ -6,9 +6,10 @@ import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.ent
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
import { GmailGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service';
import { ImapGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/imap/imap-get-all-folders.service';
import { MicrosoftGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service';
import { GmailFoldersErrorHandlerService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/services/gmail-folders-error-handler.service';
import { GmailGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/services/gmail-get-all-folders.service';
import { ImapGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/imap/services/imap-get-all-folders.service';
import { MicrosoftGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/microsoft/services/microsoft-get-all-folders.service';
import { SyncMessageFoldersService } from 'src/modules/messaging/message-folder-manager/services/sync-message-folders.service';
import { MessagingGmailDriverModule } from 'src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module';
import { MessagingIMAPDriverModule } from 'src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module';
@@ -28,6 +29,7 @@ import { MessagingMicrosoftDriverModule } from 'src/modules/messaging/message-im
providers: [
SyncMessageFoldersService,
GmailGetAllFoldersService,
GmailFoldersErrorHandlerService,
ImapGetAllFoldersService,
MicrosoftGetAllFoldersService,
],
@@ -14,9 +14,9 @@ import {
MessageFolderPendingSyncAction,
type MessageFolderWorkspaceEntity,
} from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity';
import { GmailGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/gmail-get-all-folders.service';
import { ImapGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/imap/imap-get-all-folders.service';
import { MicrosoftGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/microsoft/microsoft-get-all-folders.service';
import { GmailGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/gmail/services/gmail-get-all-folders.service';
import { ImapGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/imap/services/imap-get-all-folders.service';
import { MicrosoftGetAllFoldersService } from 'src/modules/messaging/message-folder-manager/drivers/microsoft/services/microsoft-get-all-folders.service';
import { MessageFolderName } from 'src/modules/messaging/message-import-manager/drivers/microsoft/types/folders';
type SyncMessageFoldersInput = {
@@ -17,7 +17,6 @@ import { GmailGetMessageListService } from 'src/modules/messaging/message-import
import { GmailGetMessagesService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-messages.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({
@@ -40,14 +39,12 @@ import { MessageParticipantManagerModule } from 'src/modules/messaging/message-p
GmailFetchByBatchService,
GmailGetMessagesService,
GmailGetMessageListService,
GmailNetworkErrorHandler,
GmailMessageListFetchErrorHandler,
GmailMessagesImportErrorHandler,
],
exports: [
GmailGetMessagesService,
GmailGetMessageListService,
GmailNetworkErrorHandler,
GmailMessageListFetchErrorHandler,
GmailMessagesImportErrorHandler,
],
@@ -1,196 +1,70 @@
// Gmail API Error Response Mocks for users.messages.list
import { type GmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-error.type';
const gmailApiErrorMocks = {
// 400 Bad Request - Invalid query parameters
badRequest: {
error: {
code: 400,
errors: [
{
domain: 'global',
location: 'orderBy',
locationType: 'parameter',
message:
'Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.',
reason: 'badRequest',
},
],
message:
'Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.',
},
code: '400',
message: 'badRequest',
},
// 400 Invalid Grant
invalidGrant: {
error: {
code: 400,
errors: [
{
domain: 'global',
reason: 'invalid_grant',
message: 'Invalid Credentials',
},
],
message: 'Invalid Credentials',
},
code: '400',
message: 'invalid_grant',
},
// 400 Failed Precondition
failedPrecondition: {
error: {
code: 400,
errors: [
{
domain: 'global',
reason: 'failedPrecondition',
message: 'Failed Precondition',
},
],
message: 'Failed Precondition',
},
code: '400',
message: 'failedPrecondition',
},
// 401 Invalid Credentials
invalidCredentials: {
error: {
errors: [
{
domain: 'global',
reason: 'authError',
message: 'Invalid Credentials',
locationType: 'header',
location: 'Authorization',
},
],
code: 401,
message: 'Invalid Credentials',
},
code: '401',
message: 'authError',
},
// 404 Not Found
notFound: {
error: {
errors: [
{
domain: 'global',
reason: 'notFound',
message: 'Resource not found: userId',
location: 'userId',
locationType: 'parameter',
},
],
code: 404,
message: 'Resource not found: userId',
},
code: '404',
message: 'notFound',
},
// 410 Gone
gone: {
error: {
errors: [
{
domain: 'global',
reason: 'resourceGone',
message: 'Resource has been deleted',
location: 'messageId',
locationType: 'parameter',
},
],
code: 410,
message: 'Resource has been deleted',
},
code: '410',
message: 'resourceGone',
},
// 403 Daily Limit Exceeded
dailyLimitExceeded: {
error: {
errors: [
{
domain: 'usageLimits',
reason: 'dailyLimitExceeded',
message: 'Daily Limit Exceeded',
},
],
code: 403,
message: 'Daily Limit Exceeded',
},
code: '403',
message: 'dailyLimitExceeded',
},
// 403 User Rate Limit Exceeded
userRateLimitExceeded: {
error: {
errors: [
{
domain: 'usageLimits',
reason: 'userRateLimitExceeded',
message: 'User Rate Limit Exceeded',
},
],
code: 403,
message: 'User Rate Limit Exceeded',
},
code: '403',
message: 'userRateLimitExceeded',
},
// 403 Rate Limit Exceeded
rateLimitExceeded: {
error: {
errors: [
{
domain: 'usageLimits',
reason: 'rateLimitExceeded',
message: 'Rate Limit Exceeded',
},
],
code: 403,
message: 'Rate Limit Exceeded',
},
code: '403',
message: 'rateLimitExceeded',
},
// 403 Domain Policy Error
domainPolicyError: {
error: {
errors: [
{
domain: 'global',
reason: 'domainPolicy',
message: 'The domain administrators have disabled Gmail apps.',
},
],
code: 403,
message: 'The domain administrators have disabled Gmail apps.',
},
code: '403',
message: 'domainPolicy',
},
// 429 Too Many Requests (Concurrent Requests)
tooManyConcurrentRequests: {
error: {
errors: [
{
domain: 'global',
reason: 'rateLimitExceeded',
message: 'Too many concurrent requests for user',
},
],
code: 429,
message: 'Too many concurrent requests for user',
},
code: '429',
message: 'tooManyConcurrentRequests',
},
// 500 Backend Error
backendError: {
error: {
errors: [
{
domain: 'global',
reason: 'backendError',
message: 'Backend Error',
},
],
code: 500,
message: 'Backend Error',
},
code: '500',
message: 'backendError',
},
getError: function (code: number, type?: string) {
getError: function (code: number, type?: string): GmailApiError {
switch (code) {
case 400:
switch (type) {
@@ -0,0 +1,217 @@
import { type GmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-batch-error.type';
const gmailBatchApiErrorMocks = {
// 400 Bad Request - Invalid query parameters
badRequest: {
code: 400,
errors: [
{
domain: 'global',
location: 'orderBy',
locationType: 'parameter',
message:
'Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.',
reason: 'badRequest',
},
],
message:
'Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.',
},
// 400 Invalid Grant
invalidGrant: {
code: 400,
errors: [
{
domain: 'global',
reason: 'invalid_grant',
message: 'Invalid Credentials',
},
],
message: 'Invalid Credentials',
},
// 400 Failed Precondition
failedPrecondition: {
code: 400,
errors: [
{
domain: 'global',
reason: 'failedPrecondition',
message: 'Failed Precondition',
},
],
message: 'Failed Precondition',
},
// 401 Invalid Credentials
invalidCredentials: {
errors: [
{
domain: 'global',
reason: 'authError',
message: 'Invalid Credentials',
locationType: 'header',
location: 'Authorization',
},
],
code: 401,
message: 'Invalid Credentials',
},
// 404 Not Found
notFound: {
errors: [
{
domain: 'global',
reason: 'notFound',
message: 'Resource not found: userId',
location: 'userId',
locationType: 'parameter',
},
],
code: 404,
message: 'Resource not found: userId',
},
// 410 Gone
gone: {
errors: [
{
domain: 'global',
reason: 'resourceGone',
message: 'Resource has been deleted',
location: 'messageId',
locationType: 'parameter',
},
],
code: 410,
message: 'Resource has been deleted',
},
// 403 Daily Limit Exceeded
dailyLimitExceeded: {
errors: [
{
domain: 'usageLimits',
reason: 'dailyLimitExceeded',
message: 'Daily Limit Exceeded',
},
],
code: 403,
message: 'Daily Limit Exceeded',
},
// 403 User Rate Limit Exceeded
userRateLimitExceeded: {
errors: [
{
domain: 'usageLimits',
reason: 'userRateLimitExceeded',
message: 'User Rate Limit Exceeded',
},
],
code: 403,
message: 'User Rate Limit Exceeded',
},
// 403 Rate Limit Exceeded
rateLimitExceeded: {
errors: [
{
domain: 'usageLimits',
reason: 'rateLimitExceeded',
message: 'Rate Limit Exceeded',
},
],
code: 403,
message: 'Rate Limit Exceeded',
},
// 403 Domain Policy Error
domainPolicyError: {
errors: [
{
domain: 'global',
reason: 'domainPolicy',
message: 'The domain administrators have disabled Gmail apps.',
},
],
code: 403,
message: 'The domain administrators have disabled Gmail apps.',
},
// 429 Too Many Requests (Concurrent Requests)
tooManyConcurrentRequests: {
errors: [
{
domain: 'global',
reason: 'rateLimitExceeded',
message: 'Too many concurrent requests for user',
},
],
code: 429,
message: 'Too many concurrent requests for user',
},
// 500 Backend Error
backendError: {
errors: [
{
domain: 'global',
reason: 'backendError',
message: 'Backend Error',
},
],
code: 500,
message: 'Backend Error',
},
getError: function (code: number, type?: string): GmailApiBatchError {
switch (code) {
case 400:
switch (type) {
case 'invalid_grant':
return this.invalidGrant;
case 'failedPrecondition':
return this.failedPrecondition;
default:
return this.badRequest;
}
case 401:
return this.invalidCredentials;
case 403:
switch (type) {
case 'dailyLimit':
return this.dailyLimitExceeded;
case 'userRateLimit':
return this.userRateLimitExceeded;
case 'rateLimit':
return this.rateLimitExceeded;
case 'domainPolicy':
return this.domainPolicyError;
default:
return this.rateLimitExceeded;
}
case 404:
return this.notFound;
case 410:
return this.gone;
case 429:
switch (type) {
case 'concurrent':
return this.tooManyConcurrentRequests;
case 'mailSending':
return this.mailSendingLimitExceeded;
default:
return this.tooManyConcurrentRequests;
}
case 500:
return this.backendError;
default:
throw new Error(`Unknown error code: ${code}`);
}
},
};
export default gmailBatchApiErrorMocks;
@@ -1,26 +1,33 @@
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';
import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@Injectable()
export class GmailMessageListFetchErrorHandler {
private readonly logger = new Logger(GmailMessageListFetchErrorHandler.name);
constructor(
private readonly gmailNetworkErrorHandler: GmailNetworkErrorHandler,
) {}
constructor() {}
// 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;
public handleError(error: unknown): void {
this.logger.error(`Gmail: Error fetching message list: ${error}`);
if (isGmailNetworkError(error)) {
throw parseGmailNetworkError(error);
}
throw parseGmailMessageListFetchError(error, { cause: error });
if (isGmailApiError(error)) {
throw parseGmailApiError(error);
}
throw new MessageImportDriverException(
'Unknown error',
MessageImportDriverExceptionCode.UNKNOWN,
);
}
}
@@ -1,32 +1,34 @@
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';
import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { isGmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-batch-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
import { parseGmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-batch-error.util';
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@Injectable()
export class GmailMessagesImportErrorHandler {
private readonly logger = new Logger(GmailMessagesImportErrorHandler.name);
constructor(
private readonly gmailNetworkErrorHandler: GmailNetworkErrorHandler,
) {}
constructor() {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public handleError(error: any, messageExternalId: string): void {
this.logger.log(`Error fetching messages`, error);
public handleError(error: unknown, messageExternalId: string): void {
this.logger.error(`Gmail: Error importing messages: ${error}`);
const networkError = this.gmailNetworkErrorHandler.handleError(error);
if (networkError) {
throw networkError;
if (isGmailNetworkError(error)) {
throw parseGmailNetworkError(error);
}
const gmailError = parseGmailMessagesImportError(error, messageExternalId, {
cause: error,
});
if (gmailError) {
throw gmailError;
if (isGmailApiBatchError(error)) {
throw parseGmailApiBatchError(error, messageExternalId);
}
throw new MessageImportDriverException(
'Unknown error',
MessageImportDriverExceptionCode.UNKNOWN,
);
}
}
@@ -1,23 +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';
@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;
}
}
@@ -0,0 +1,7 @@
export type GmailApiBatchError = {
code: number;
errors: {
reason: string;
message: string;
}[];
};
@@ -0,0 +1,4 @@
export type GmailApiError = {
code: string;
message: string;
};
@@ -1,45 +1,45 @@
import gaxiosErrorMocks from 'src/modules/messaging/message-import-manager/drivers/gmail/mocks/gaxios-error-mocks';
import { isAxiosTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-axios-gaxios-error.util';
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
describe('parseGaxiosError', () => {
describe('isGmailNetworkError', () => {
it('should return a MessageImportDriverException for ECONNRESET', () => {
const error = gaxiosErrorMocks.getError('ECONNRESET');
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(true);
});
it('should return a MessageImportDriverException for ENOTFOUND', () => {
const error = gaxiosErrorMocks.getError('ENOTFOUND');
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(true);
});
it('should return a MessageImportDriverException for ECONNABORTED', () => {
const error = gaxiosErrorMocks.getError('ECONNABORTED');
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(true);
});
it('should return a MessageImportDriverException for ETIMEDOUT', () => {
const error = gaxiosErrorMocks.getError('ETIMEDOUT');
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(true);
});
it('should return a MessageImportDriverException for ERR_NETWORK', () => {
const error = gaxiosErrorMocks.getError('ERR_NETWORK');
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(true);
});
it('should return undefined for unknown error codes', () => {
const error = { code: 'UNKNOWN_ERROR' } as any;
const result = isAxiosTemporaryError(error);
const result = isGmailNetworkError(error);
expect(result).toBe(false);
});
@@ -3,12 +3,12 @@ import {
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import gmailApiErrorMocks from 'src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks';
import { parseGmailMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util';
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
describe('parseGmailMessageListFetchError', () => {
describe('parseGmailApiError', () => {
it('should handle 400 Bad Request', () => {
const error = gmailApiErrorMocks.getError(400);
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(MessageImportDriverExceptionCode.UNKNOWN);
@@ -16,7 +16,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 400 Invalid Grant', () => {
const error = gmailApiErrorMocks.getError(400, 'invalid_grant');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -26,7 +26,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 400 Failed Precondition', () => {
const error = gmailApiErrorMocks.getError(400, 'failedPrecondition');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -36,7 +36,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 401 Invalid Credentials', () => {
const error = gmailApiErrorMocks.getError(401);
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -46,7 +46,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 403 Daily Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'dailyLimit');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -56,7 +56,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 403 User Rate Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'userRateLimit');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -66,7 +66,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 403 Rate Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'rateLimit');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -76,7 +76,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 403 Domain Policy Error', () => {
const error = gmailApiErrorMocks.getError(403, 'domainPolicy');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -86,7 +86,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 404 as sync cursor error', () => {
const error = gmailApiErrorMocks.getError(404);
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -96,7 +96,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 410 Gone', () => {
const error = gmailApiErrorMocks.getError(410);
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(MessageImportDriverExceptionCode.UNKNOWN);
@@ -104,7 +104,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 429 Too Many Requests', () => {
const error = gmailApiErrorMocks.getError(429, 'concurrent');
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -114,7 +114,7 @@ describe('parseGmailMessageListFetchError', () => {
it('should handle 500 Backend Error', () => {
const error = gmailApiErrorMocks.getError(500);
const exception = parseGmailMessageListFetchError(error.error);
const exception = parseGmailApiError(error);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception.code).toBe(
@@ -2,48 +2,39 @@ import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import gmailApiErrorMocks from 'src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-api-error-mocks';
import { parseGmailMessagesImportError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-messages-import-error.util';
import { default as gmailBatchApiErrorMocks } from 'src/modules/messaging/message-import-manager/drivers/gmail/mocks/gmail-batch-api-error-mocks';
import { parseGmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-batch-error.util';
const messageExternalId = '123';
describe('parseGmailMessagesImportError', () => {
describe('parseGmailApiBatchError', () => {
it('should handle 400 Bad Request', () => {
const error = gmailApiErrorMocks.getError(400);
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(400);
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(MessageImportDriverExceptionCode.UNKNOWN);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 400 Invalid Grant', () => {
const error = gmailApiErrorMocks.getError(400, 'invalid_grant');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(400, 'invalid_grant');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 400 Failed Precondition', () => {
const error = gmailApiErrorMocks.getError(400, 'failedPrecondition');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(400, 'failedPrecondition');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
@@ -52,146 +43,119 @@ describe('parseGmailMessagesImportError', () => {
});
it('should handle 401 Invalid Credentials', () => {
const error = gmailApiErrorMocks.getError(401);
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(401);
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 403 Daily Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'dailyLimit');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(403, 'dailyLimit');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 403 User Rate Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'userRateLimit');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(403, 'userRateLimit');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 403 Rate Limit Exceeded', () => {
const error = gmailApiErrorMocks.getError(403, 'rateLimit');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(403, 'rateLimit');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 403 Domain Policy Error', () => {
const error = gmailApiErrorMocks.getError(403, 'domainPolicy');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(403, 'domainPolicy');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 404 Not Found', () => {
const error = gmailApiErrorMocks.getError(404);
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(404);
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 410 Gone', () => {
const error = gmailApiErrorMocks.getError(410);
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(410);
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 429 Too Many Requests', () => {
const error = gmailApiErrorMocks.getError(429, 'concurrent');
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(429, 'concurrent');
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
it('should handle 500 Backend Error', () => {
const error = gmailApiErrorMocks.getError(500);
const exception = parseGmailMessagesImportError(
error.error,
messageExternalId,
);
const error = gmailBatchApiErrorMocks.getError(500);
const exception = parseGmailApiBatchError(error, messageExternalId);
expect(exception).toBeInstanceOf(MessageImportDriverException);
expect(exception?.code).toBe(
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
);
expect(exception?.message).toBe(
`${error.error.errors[0].message} for message with externalId: ${messageExternalId}`,
`${error.errors[0].message} for message with externalId: ${messageExternalId}`,
);
});
});
@@ -0,0 +1,21 @@
import { type GmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-batch-error.type';
export const isGmailApiBatchError = (
error: unknown,
): error is GmailApiBatchError => {
if (error === null || typeof error !== 'object') {
return false;
}
if (
!('code' in error) ||
!('errors' in error) ||
!Array.isArray(error.errors) ||
error.errors.length === 0 ||
error.errors.some((error) => !('reason' in error) || !('message' in error))
) {
return false;
}
return true;
};
@@ -0,0 +1,18 @@
import { type GmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-error.type';
export const isGmailApiError = (error: unknown): error is GmailApiError => {
if (error === null || typeof error !== 'object') {
return false;
}
if (
!('code' in error) ||
typeof error.code !== 'string' ||
!('message' in error) ||
typeof error.message !== 'string'
) {
return false;
}
return true;
};
@@ -2,10 +2,16 @@ import { type GaxiosError } from 'gaxios';
import { MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception';
export const isAxiosTemporaryError = (error: GaxiosError): boolean => {
const { code } = error;
export const isGmailNetworkError = (error: unknown): error is GaxiosError => {
if (error === null || typeof error !== 'object') {
return false;
}
switch (code) {
if (!('code' in error)) {
return false;
}
switch (error.code) {
case MessageNetworkExceptionCode.ECONNRESET:
case MessageNetworkExceptionCode.ENOTFOUND:
case MessageNetworkExceptionCode.ECONNABORTED:
@@ -13,7 +19,6 @@ export const isAxiosTemporaryError = (error: GaxiosError): boolean => {
case MessageNetworkExceptionCode.ERR_NETWORK:
case MessageNetworkExceptionCode.EHOSTUNREACH:
return true;
default:
return false;
}
@@ -2,17 +2,11 @@ import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { type GmailApiBatchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-batch-error.type';
export const parseGmailMessagesImportError = (
error: {
code?: number;
errors: {
reason: string;
message: string;
}[];
},
messageExternalId: string,
options?: { cause?: Error },
export const parseGmailApiBatchError = (
error: GmailApiBatchError,
messageExternalId?: string,
): MessageImportDriverException | undefined => {
const { code, errors } = error;
@@ -26,7 +20,6 @@ export const parseGmailMessagesImportError = (
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
{ cause: options?.cause },
);
}
if (reason === 'failedPrecondition') {
@@ -34,21 +27,18 @@ 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:
@@ -56,14 +46,12 @@ export const parseGmailMessagesImportError = (
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR,
{ cause: options?.cause },
);
case 429:
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
case 403:
@@ -75,14 +63,12 @@ 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 },
);
}
@@ -92,14 +78,12 @@ 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:
@@ -109,7 +93,6 @@ export const parseGmailMessagesImportError = (
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
}
@@ -117,7 +100,6 @@ export const parseGmailMessagesImportError = (
return new MessageImportDriverException(
`${code} - ${reason} - ${message}`,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
}
break;
@@ -129,6 +111,5 @@ export const parseGmailMessagesImportError = (
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.UNKNOWN,
{ cause: options?.cause },
);
};
@@ -2,119 +2,98 @@ import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { type GmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-api-error.type';
export const parseGmailMessageListFetchError = (
error: {
code?: number;
errors: {
reason: string;
message: string;
}[];
},
options?: { cause?: Error },
export const parseGmailApiError = (
error: GmailApiError,
): MessageImportDriverException => {
const { code, errors } = error;
const reason = errors?.[0]?.reason;
const message = errors?.[0]?.message;
const { code, message } = error;
switch (code) {
case 400:
if (reason === 'invalid_grant') {
case '400':
if (message === 'invalid_grant') {
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
{ cause: options?.cause },
);
}
if (reason === 'failedPrecondition') {
if (message === 'failedPrecondition') {
if (message.includes('Mail service not enabled')) {
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:
case '404':
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR,
{ cause: options?.cause },
);
case 429:
case '429':
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
case 403:
case '403':
if (
reason === 'rateLimitExceeded' ||
reason === 'userRateLimitExceeded' ||
reason === 'dailyLimitExceeded'
message === 'rateLimitExceeded' ||
message === 'userRateLimitExceeded' ||
message === 'dailyLimitExceeded'
) {
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
}
if (reason === 'domainPolicy') {
if (message === 'domainPolicy') {
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
{ cause: options?.cause },
);
}
break;
case 401:
case '401':
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
{ cause: options?.cause },
);
case 503:
case '503':
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
case 500:
case 502:
case 504:
if (reason === 'backendError') {
case '500':
case '502':
case '504':
if (message === 'backendError') {
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
}
if (errors?.[0]?.message.includes(`Authentication backend unavailable`)) {
if (message.includes(`Authentication backend unavailable`)) {
return new MessageImportDriverException(
`${code} - ${reason} - ${message}`,
`${code} - ${message}`,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: options?.cause },
);
}
break;
@@ -126,6 +105,5 @@ export const parseGmailMessageListFetchError = (
return new MessageImportDriverException(
message,
MessageImportDriverExceptionCode.UNKNOWN,
{ cause: options?.cause },
);
};
@@ -0,0 +1,16 @@
import { type GaxiosError } from 'gaxios';
import {
MessageImportDriverException,
MessageImportDriverExceptionCode,
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
export const parseGmailNetworkError = (
error: GaxiosError,
): MessageImportDriverException | null => {
return new MessageImportDriverException(
error.message,
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
{ cause: error },
);
};