Catching "no licence" microsoft account (#12143)
Catching "no licence - removed" microsoft message channels. Current behabiour > ` MessageImportException [Error]: The mailbox is either inactive, soft-deleted, or is hosted on-premise.` Goal: better track errors VS user mistakes Context: A similar logic was already implemented for the calendar channels. I just replicated it to message channels
This commit is contained in:
@ -4,6 +4,7 @@ import {
|
||||
CalendarEventImportDriverException,
|
||||
CalendarEventImportDriverExceptionCode,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/drivers/exceptions/calendar-event-import-driver.exception';
|
||||
import { MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception';
|
||||
|
||||
export const parseGaxiosError = (
|
||||
error: GaxiosError,
|
||||
@ -11,11 +12,11 @@ export const parseGaxiosError = (
|
||||
const { code } = error;
|
||||
|
||||
switch (code) {
|
||||
case 'ECONNRESET':
|
||||
case 'ENOTFOUND':
|
||||
case 'ECONNABORTED':
|
||||
case 'ETIMEDOUT':
|
||||
case 'ERR_NETWORK':
|
||||
case MessageNetworkExceptionCode.ECONNRESET:
|
||||
case MessageNetworkExceptionCode.ENOTFOUND:
|
||||
case MessageNetworkExceptionCode.ECONNABORTED:
|
||||
case MessageNetworkExceptionCode.ETIMEDOUT:
|
||||
case MessageNetworkExceptionCode.ERR_NETWORK:
|
||||
return new CalendarEventImportDriverException(
|
||||
error.message,
|
||||
CalendarEventImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
export enum MessageNetworkExceptionCode {
|
||||
ECONNRESET = 'ECONNRESET',
|
||||
ENOTFOUND = 'ENOTFOUND',
|
||||
ECONNABORTED = 'ECONNABORTED',
|
||||
ETIMEDOUT = 'ETIMEDOUT',
|
||||
ERR_NETWORK = 'ERR_NETWORK',
|
||||
}
|
||||
@ -4,6 +4,7 @@ import {
|
||||
MessageImportDriverException,
|
||||
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';
|
||||
|
||||
export const parseGaxiosError = (
|
||||
error: GaxiosError,
|
||||
@ -11,11 +12,11 @@ export const parseGaxiosError = (
|
||||
const { code } = error;
|
||||
|
||||
switch (code) {
|
||||
case 'ECONNRESET':
|
||||
case 'ENOTFOUND':
|
||||
case 'ECONNABORTED':
|
||||
case 'ETIMEDOUT':
|
||||
case 'ERR_NETWORK':
|
||||
case MessageNetworkExceptionCode.ECONNRESET:
|
||||
case MessageNetworkExceptionCode.ENOTFOUND:
|
||||
case MessageNetworkExceptionCode.ECONNABORTED:
|
||||
case MessageNetworkExceptionCode.ETIMEDOUT:
|
||||
case MessageNetworkExceptionCode.ERR_NETWORK:
|
||||
return new MessageImportDriverException(
|
||||
error.message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { MicrosoftImportDriverException } from 'src/modules/messaging/message-import-manager/drivers/microsoft/exceptions/microsoft-import-driver.exception';
|
||||
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
|
||||
import { MicrosoftGraphBatchResponse } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.interface';
|
||||
import { isMicrosoftClientTemporaryError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-temporary-error.utils';
|
||||
import { MicrosoftHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service';
|
||||
|
||||
@Injectable()
|
||||
export class MicrosoftFetchByBatchService {
|
||||
private readonly logger = new Logger(MicrosoftFetchByBatchService.name);
|
||||
constructor(
|
||||
private readonly microsoftClientProvider: MicrosoftClientProvider,
|
||||
private readonly microsoftHandleErrorService: MicrosoftHandleErrorService,
|
||||
) {}
|
||||
|
||||
async fetchAllByBatches(
|
||||
@ -52,25 +52,9 @@ export class MicrosoftFetchByBatchService {
|
||||
|
||||
batchResponses.push(batchResponse);
|
||||
} catch (error) {
|
||||
if (
|
||||
error.body &&
|
||||
typeof error.body === 'string' &&
|
||||
isMicrosoftClientTemporaryError(error.body)
|
||||
) {
|
||||
// TODO: remove this log once we catch better the error codes
|
||||
this.logger.error(
|
||||
`Error temporary (${error.code}) fetching messages for account ${connectedAccount.id.slice(0, 8)}`,
|
||||
);
|
||||
this.logger.log(error);
|
||||
throw new MicrosoftImportDriverException(error.body, error.code, 429);
|
||||
} else {
|
||||
// TODO: remove this log once we catch better the error codes
|
||||
this.logger.error(
|
||||
`Error unknown (${error.code}) fetching messages for account ${connectedAccount.id.slice(0, 8)}`,
|
||||
);
|
||||
this.logger.log(error);
|
||||
throw error;
|
||||
}
|
||||
this.microsoftHandleErrorService.handleMicrosoftMessageFetchByBatchError(
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +77,12 @@ export class MicrosoftGetMessageListService {
|
||||
.headers({
|
||||
Prefer: `odata.maxpagesize=${MESSAGING_MICROSOFT_USERS_MESSAGES_LIST_MAX_RESULT}, IdType="ImmutableId"`,
|
||||
})
|
||||
.get();
|
||||
.get()
|
||||
.catch((error) => {
|
||||
this.microsoftHandleErrorService.handleMicrosoftGetMessageListError(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
const callback: PageIteratorCallback = (data) => {
|
||||
messageExternalIds.push(data.id);
|
||||
@ -92,7 +97,9 @@ export class MicrosoftGetMessageListService {
|
||||
});
|
||||
|
||||
await pageIterator.iterate().catch((error) => {
|
||||
this.microsoftHandleErrorService.handleMicrosoftMessageFetchError(error);
|
||||
this.microsoftHandleErrorService.handleMicrosoftGetMessageListError(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
@ -195,7 +202,12 @@ export class MicrosoftGetMessageListService {
|
||||
.headers({
|
||||
Prefer: `odata.maxpagesize=${MESSAGING_MICROSOFT_USERS_MESSAGES_LIST_MAX_RESULT}, IdType="ImmutableId"`,
|
||||
})
|
||||
.get();
|
||||
.get()
|
||||
.catch((error) => {
|
||||
this.microsoftHandleErrorService.handleMicrosoftGetMessageListError(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
const callback: PageIteratorCallback = (data) => {
|
||||
if (data['@removed']) {
|
||||
@ -214,7 +226,9 @@ export class MicrosoftGetMessageListService {
|
||||
});
|
||||
|
||||
await pageIterator.iterate().catch((error) => {
|
||||
this.microsoftHandleErrorService.handleMicrosoftMessageFetchError(error);
|
||||
this.microsoftHandleErrorService.handleMicrosoftGetMessageListError(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -4,6 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { computeMessageDirection } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/compute-message-direction.util';
|
||||
import { MicrosoftImportDriverException } from 'src/modules/messaging/message-import-manager/drivers/microsoft/exceptions/microsoft-import-driver.exception';
|
||||
import { MicrosoftGraphBatchResponse } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.interface';
|
||||
import { MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message';
|
||||
import { formatAddressObjectAsParticipants } from 'src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util';
|
||||
@ -43,7 +44,7 @@ export class MicrosoftGetMessagesService {
|
||||
|
||||
return messages;
|
||||
} catch (error) {
|
||||
this.microsoftHandleErrorService.handleMicrosoftMessageFetchError(error);
|
||||
this.microsoftHandleErrorService.handleMicrosoftGetMessagesError(error);
|
||||
|
||||
return [];
|
||||
}
|
||||
@ -69,8 +70,10 @@ export class MicrosoftGetMessagesService {
|
||||
|
||||
const messages = parsedResponses.map((response) => {
|
||||
if ('error' in response) {
|
||||
this.microsoftHandleErrorService.throwMicrosoftBatchError(
|
||||
response.error,
|
||||
throw new MicrosoftImportDriverException(
|
||||
response.error.message,
|
||||
response.error.code,
|
||||
response.error.statusCode,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
MessageImportDriverException,
|
||||
MessageImportDriverExceptionCode,
|
||||
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
|
||||
import { MicrosoftImportDriverException } from 'src/modules/messaging/message-import-manager/drivers/microsoft/exceptions/microsoft-import-driver.exception';
|
||||
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);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public handleMicrosoftMessageFetchError(error: any): void {
|
||||
public handleMicrosoftMessageFetchByBatchError(error: any): void {
|
||||
// TODO: remove this log once we catch better the error codes
|
||||
this.logger.error(`Error temporary (${error.code}) fetching messages`);
|
||||
this.logger.log(error);
|
||||
|
||||
const isBodyString = error.body && typeof error.body === 'string';
|
||||
const isTemporaryError =
|
||||
isBodyString && isMicrosoftClientTemporaryError(error.body);
|
||||
|
||||
if (isTemporaryError) {
|
||||
throw new MessageImportDriverException(
|
||||
`code: ${error.code} - body: ${error.body}`,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
if (!error.statusCode) {
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft Graph API unknown error: ${error}`,
|
||||
@ -17,25 +35,10 @@ export class MicrosoftHandleErrorService {
|
||||
);
|
||||
}
|
||||
|
||||
if (error.statusCode === 401) {
|
||||
throw new MessageImportDriverException(
|
||||
'Unauthorized access to Microsoft Graph API',
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
const exception = parseMicrosoftMessagesImportError(error);
|
||||
|
||||
if (error.statusCode === 403) {
|
||||
throw new MessageImportDriverException(
|
||||
'Forbidden access to Microsoft Graph API',
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
|
||||
if (error.statusCode === 429) {
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft Graph API ${error.code} ${error.statusCode} error: ${error.message}`,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
if (exception) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
throw new MessageImportDriverException(
|
||||
@ -45,11 +48,44 @@ export class MicrosoftHandleErrorService {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public throwMicrosoftBatchError(error: any): void {
|
||||
throw new MicrosoftImportDriverException(
|
||||
error.message,
|
||||
error.code,
|
||||
error.statusCode,
|
||||
public handleMicrosoftGetMessageListError(error: any): void {
|
||||
if (!error.statusCode) {
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft Graph API unknown error: ${error}`,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
|
||||
const exception = parseMicrosoftMessagesImportError(error);
|
||||
|
||||
if (exception) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft driver error: ${error.message}`,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public handleMicrosoftGetMessagesError(error: any): void {
|
||||
if (!error.statusCode) {
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft Graph API unknown error: ${error}`,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
|
||||
const exception = parseMicrosoftMessagesImportError(error);
|
||||
|
||||
if (exception) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
throw new MessageImportDriverException(
|
||||
`Microsoft driver error: ${error.message}`,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import {
|
||||
MessageImportDriverException,
|
||||
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 | undefined => {
|
||||
if (error.statusCode === 401) {
|
||||
return new MessageImportDriverException(
|
||||
'Unauthorized access to Microsoft Graph API',
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
|
||||
if (error.statusCode === 403) {
|
||||
return new MessageImportDriverException(
|
||||
'Forbidden access to Microsoft Graph API',
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
|
||||
if (error.statusCode === 404) {
|
||||
if (
|
||||
error.message?.includes(
|
||||
'The mailbox is either inactive, soft-deleted, or is hosted on-premise.',
|
||||
)
|
||||
) {
|
||||
return new MessageImportDriverException(
|
||||
`Disabled, deleted, inactive or no licence Microsoft account - code:${error.code}`,
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
} else {
|
||||
return new MessageImportDriverException(
|
||||
`Not found - code:${error.code}`,
|
||||
MessageImportDriverExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.statusCode === 429) {
|
||||
return new MessageImportDriverException(
|
||||
`Microsoft Graph API ${error.code} ${error.statusCode} error: ${error.message}`,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
@ -12,6 +12,7 @@ import {
|
||||
MessageImportDriverException,
|
||||
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,
|
||||
@ -51,6 +52,11 @@ export class MessageImportExceptionHandlerService {
|
||||
);
|
||||
break;
|
||||
case MessageImportDriverExceptionCode.TEMPORARY_ERROR:
|
||||
case MessageNetworkExceptionCode.ECONNABORTED:
|
||||
case MessageNetworkExceptionCode.ENOTFOUND:
|
||||
case MessageNetworkExceptionCode.ECONNRESET:
|
||||
case MessageNetworkExceptionCode.ETIMEDOUT:
|
||||
case MessageNetworkExceptionCode.ERR_NETWORK:
|
||||
await this.handleTemporaryException(
|
||||
syncStep,
|
||||
messageChannel,
|
||||
|
||||
Reference in New Issue
Block a user