mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-09 02:11:55 +03:00
8733 refactor gmailhandleerrorservice (#8901)
Closes #8733 - Refactor `GmailHandleErrorService` - Add tests and mocks for the errors
This commit is contained in:
parent
680366e998
commit
de56c01206
@ -5,8 +5,8 @@ import chunk from 'lodash.chunk';
|
||||
import compact from 'lodash.compact';
|
||||
import { Any, EntityManager, Repository } from 'typeorm';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
@ -24,7 +24,6 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { isWorkEmail } from 'src/utils/is-work-email';
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
|
||||
@Injectable()
|
||||
export class CreateCompanyAndContactService {
|
||||
@ -36,8 +35,6 @@ export class CreateCompanyAndContactService {
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
|
@ -0,0 +1,117 @@
|
||||
// Gaxios Network Error Mocks
|
||||
const gaxiosErrorMocks = {
|
||||
// Connection Reset Error
|
||||
connectionReset: {
|
||||
code: 'ECONNRESET',
|
||||
name: 'GaxiosError',
|
||||
message: 'socket hang up',
|
||||
status: null,
|
||||
config: {
|
||||
method: 'GET',
|
||||
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
|
||||
headers: {
|
||||
Authorization: 'Bearer [TOKEN]',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 5000,
|
||||
responseType: 'json',
|
||||
},
|
||||
response: undefined,
|
||||
},
|
||||
|
||||
// Host Not Found Error
|
||||
hostNotFound: {
|
||||
code: 'ENOTFOUND',
|
||||
name: 'GaxiosError',
|
||||
message: 'getaddrinfo ENOTFOUND gmail.googleapis.com',
|
||||
status: null,
|
||||
config: {
|
||||
method: 'GET',
|
||||
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
|
||||
headers: {
|
||||
Authorization: 'Bearer [TOKEN]',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 5000,
|
||||
responseType: 'json',
|
||||
},
|
||||
response: undefined,
|
||||
},
|
||||
|
||||
// Connection Aborted Error
|
||||
connectionAborted: {
|
||||
code: 'ECONNABORTED',
|
||||
name: 'GaxiosError',
|
||||
message: 'The request was aborted due to a timeout',
|
||||
status: null,
|
||||
config: {
|
||||
method: 'GET',
|
||||
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
|
||||
headers: {
|
||||
Authorization: 'Bearer [TOKEN]',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 5000,
|
||||
responseType: 'json',
|
||||
},
|
||||
response: undefined,
|
||||
},
|
||||
|
||||
// Timeout Error
|
||||
timeout: {
|
||||
code: 'ETIMEDOUT',
|
||||
name: 'GaxiosError',
|
||||
message: 'Connection timed out after 5000ms',
|
||||
status: null,
|
||||
config: {
|
||||
method: 'GET',
|
||||
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
|
||||
headers: {
|
||||
Authorization: 'Bearer [TOKEN]',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 5000,
|
||||
responseType: 'json',
|
||||
},
|
||||
response: undefined,
|
||||
},
|
||||
|
||||
// Network Error
|
||||
networkError: {
|
||||
code: 'ERR_NETWORK',
|
||||
name: 'GaxiosError',
|
||||
message: 'Network Error',
|
||||
status: null,
|
||||
config: {
|
||||
method: 'GET',
|
||||
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
|
||||
headers: {
|
||||
Authorization: 'Bearer [TOKEN]',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 5000,
|
||||
responseType: 'json',
|
||||
},
|
||||
response: undefined,
|
||||
},
|
||||
|
||||
// Helper function to get error by code
|
||||
getError: function (code: string) {
|
||||
switch (code) {
|
||||
case 'ECONNRESET':
|
||||
return this.connectionReset;
|
||||
case 'ENOTFOUND':
|
||||
return this.hostNotFound;
|
||||
case 'ECONNABORTED':
|
||||
return this.connectionAborted;
|
||||
case 'ETIMEDOUT':
|
||||
return this.timeout;
|
||||
case 'ERR_NETWORK':
|
||||
return this.networkError;
|
||||
default:
|
||||
throw new Error(`Unknown error code: ${code}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default gaxiosErrorMocks;
|
@ -0,0 +1,240 @@
|
||||
// Gmail API Error Response Mocks for users.messages.list
|
||||
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.',
|
||||
},
|
||||
},
|
||||
|
||||
// 400 Invalid Grant
|
||||
invalidGrant: {
|
||||
error: {
|
||||
code: 400,
|
||||
errors: [
|
||||
{
|
||||
domain: 'global',
|
||||
reason: 'invalid_grant',
|
||||
message: 'Invalid Credentials',
|
||||
},
|
||||
],
|
||||
message: 'Invalid Credentials',
|
||||
},
|
||||
},
|
||||
|
||||
// 400 Failed Precondition
|
||||
failedPrecondition: {
|
||||
error: {
|
||||
code: 400,
|
||||
errors: [
|
||||
{
|
||||
domain: 'global',
|
||||
reason: 'failedPrecondition',
|
||||
message: 'Failed Precondition',
|
||||
},
|
||||
],
|
||||
message: 'Failed Precondition',
|
||||
},
|
||||
},
|
||||
|
||||
// 401 Invalid Credentials
|
||||
invalidCredentials: {
|
||||
error: {
|
||||
errors: [
|
||||
{
|
||||
domain: 'global',
|
||||
reason: 'authError',
|
||||
message: 'Invalid Credentials',
|
||||
locationType: 'header',
|
||||
location: 'Authorization',
|
||||
},
|
||||
],
|
||||
code: 401,
|
||||
message: 'Invalid Credentials',
|
||||
},
|
||||
},
|
||||
|
||||
// 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',
|
||||
},
|
||||
},
|
||||
|
||||
// 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',
|
||||
},
|
||||
},
|
||||
|
||||
// 403 Daily Limit Exceeded
|
||||
dailyLimitExceeded: {
|
||||
error: {
|
||||
errors: [
|
||||
{
|
||||
domain: 'usageLimits',
|
||||
reason: 'dailyLimitExceeded',
|
||||
message: 'Daily Limit Exceeded',
|
||||
},
|
||||
],
|
||||
code: 403,
|
||||
message: 'Daily Limit Exceeded',
|
||||
},
|
||||
},
|
||||
|
||||
// 403 User Rate Limit Exceeded
|
||||
userRateLimitExceeded: {
|
||||
error: {
|
||||
errors: [
|
||||
{
|
||||
domain: 'usageLimits',
|
||||
reason: 'userRateLimitExceeded',
|
||||
message: 'User Rate Limit Exceeded',
|
||||
},
|
||||
],
|
||||
code: 403,
|
||||
message: 'User Rate Limit Exceeded',
|
||||
},
|
||||
},
|
||||
|
||||
// 403 Rate Limit Exceeded
|
||||
rateLimitExceeded: {
|
||||
error: {
|
||||
errors: [
|
||||
{
|
||||
domain: 'usageLimits',
|
||||
reason: 'rateLimitExceeded',
|
||||
message: 'Rate Limit Exceeded',
|
||||
},
|
||||
],
|
||||
code: 403,
|
||||
message: 'Rate Limit Exceeded',
|
||||
},
|
||||
},
|
||||
|
||||
// 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.',
|
||||
},
|
||||
},
|
||||
|
||||
// 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',
|
||||
},
|
||||
},
|
||||
|
||||
// 500 Backend Error
|
||||
backendError: {
|
||||
error: {
|
||||
errors: [
|
||||
{
|
||||
domain: 'global',
|
||||
reason: 'backendError',
|
||||
message: 'Backend Error',
|
||||
},
|
||||
],
|
||||
code: 500,
|
||||
message: 'Backend Error',
|
||||
},
|
||||
},
|
||||
|
||||
getError: function (code: number, type?: string) {
|
||||
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 gmailApiErrorMocks;
|
@ -36,7 +36,7 @@ export class GmailGetHistoryService {
|
||||
labelId,
|
||||
})
|
||||
.catch((error) => {
|
||||
this.gmailHandleErrorService.handleError(error);
|
||||
this.gmailHandleErrorService.handleGmailMessageListFetchError(error);
|
||||
|
||||
return {
|
||||
data: {
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
GetPartialMessageListResponse,
|
||||
} from 'src/modules/messaging/message-import-manager/services/messaging-get-message-list.service';
|
||||
import { assertNotNull } from 'src/utils/assert';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Injectable()
|
||||
export class GmailGetMessageListService {
|
||||
@ -53,7 +54,7 @@ export class GmailGetMessageListService {
|
||||
),
|
||||
})
|
||||
.catch((error) => {
|
||||
this.gmailHandleErrorService.handleError(error);
|
||||
this.gmailHandleErrorService.handleGmailMessageListFetchError(error);
|
||||
|
||||
return {
|
||||
data: {
|
||||
@ -79,13 +80,23 @@ export class GmailGetMessageListService {
|
||||
messageExternalIds.push(...messages.map((message) => message.id));
|
||||
}
|
||||
|
||||
if (!isDefined(firstMessageExternalId)) {
|
||||
throw new MessageImportDriverException(
|
||||
`No firstMessageExternalId found for connected account ${connectedAccount.id}`,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
|
||||
const firstMessageContent = await gmailClient.users.messages
|
||||
.get({
|
||||
userId: 'me',
|
||||
id: firstMessageExternalId,
|
||||
})
|
||||
.catch((error) => {
|
||||
this.gmailHandleErrorService.handleError(error);
|
||||
this.gmailHandleErrorService.handleGmailMessagesImportError(
|
||||
error,
|
||||
firstMessageExternalId as string,
|
||||
);
|
||||
});
|
||||
|
||||
const nextSyncCursor = firstMessageContent?.data?.historyId;
|
||||
|
@ -77,11 +77,7 @@ export class GmailGetMessagesService {
|
||||
|
||||
const messages = parsedResponses.map((response, index) => {
|
||||
if ('error' in response) {
|
||||
if (response.error.code === 404) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.gmailHandleErrorService.handleError(
|
||||
this.gmailHandleErrorService.handleGmailMessagesImportError(
|
||||
response.error,
|
||||
messageIds[index],
|
||||
);
|
||||
|
@ -1,33 +1,37 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { parseGaxiosError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gaxios-error.util';
|
||||
import { parseGmailError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-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() {}
|
||||
|
||||
public handleError(error: any, messageExternalId?: string): void {
|
||||
if (
|
||||
error.code &&
|
||||
[
|
||||
'ECONNRESET',
|
||||
'ENOTFOUND',
|
||||
'ECONNABORTED',
|
||||
'ETIMEDOUT',
|
||||
'ERR_NETWORK',
|
||||
].includes(error.code)
|
||||
) {
|
||||
throw parseGaxiosError(error);
|
||||
}
|
||||
if (error.code != 410) {
|
||||
const gmailError = {
|
||||
code: error.code,
|
||||
reason: `${error?.errors?.[0].reason || error.response?.data?.error || ''}`,
|
||||
message: `${error?.errors?.[0].message || error.response?.data?.error_description || ''}${messageExternalId ? ` for message with externalId: ${messageExternalId}` : ''}`,
|
||||
};
|
||||
public handleGmailMessageListFetchError(error: any): void {
|
||||
const gaxiosError = parseGaxiosError(error);
|
||||
|
||||
throw parseGmailError(gmailError);
|
||||
if (gaxiosError) {
|
||||
throw gaxiosError;
|
||||
}
|
||||
|
||||
throw parseGmailMessageListFetchError(error);
|
||||
}
|
||||
|
||||
public handleGmailMessagesImportError(
|
||||
error: any,
|
||||
messageExternalId: string,
|
||||
): void {
|
||||
const gaxiosError = parseGaxiosError(error);
|
||||
|
||||
if (gaxiosError) {
|
||||
throw gaxiosError;
|
||||
}
|
||||
|
||||
const gmailError = parseGmailMessagesImportError(error, messageExternalId);
|
||||
|
||||
if (gmailError) {
|
||||
throw gmailError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
import {
|
||||
MessageImportDriverException,
|
||||
MessageImportDriverExceptionCode,
|
||||
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
|
||||
import gaxiosErrorMocks from 'src/modules/messaging/message-import-manager/drivers/gmail/mocks/gaxios-error-mocks';
|
||||
import { parseGaxiosError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gaxios-error.util';
|
||||
|
||||
describe('parseGaxiosError', () => {
|
||||
it('should return a MessageImportDriverException for ECONNRESET', () => {
|
||||
const error = gaxiosErrorMocks.getError('ECONNRESET');
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(result?.message).toBe(error.message);
|
||||
expect(result?.code).toBe(MessageImportDriverExceptionCode.TEMPORARY_ERROR);
|
||||
});
|
||||
|
||||
it('should return a MessageImportDriverException for ENOTFOUND', () => {
|
||||
const error = gaxiosErrorMocks.getError('ENOTFOUND');
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(result?.message).toBe(error.message);
|
||||
expect(result?.code).toBe(MessageImportDriverExceptionCode.TEMPORARY_ERROR);
|
||||
});
|
||||
|
||||
it('should return a MessageImportDriverException for ECONNABORTED', () => {
|
||||
const error = gaxiosErrorMocks.getError('ECONNABORTED');
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(result?.message).toBe(error.message);
|
||||
expect(result?.code).toBe(MessageImportDriverExceptionCode.TEMPORARY_ERROR);
|
||||
});
|
||||
|
||||
it('should return a MessageImportDriverException for ETIMEDOUT', () => {
|
||||
const error = gaxiosErrorMocks.getError('ETIMEDOUT');
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(result?.message).toBe(error.message);
|
||||
expect(result?.code).toBe(MessageImportDriverExceptionCode.TEMPORARY_ERROR);
|
||||
});
|
||||
|
||||
it('should return a MessageImportDriverException for ERR_NETWORK', () => {
|
||||
const error = gaxiosErrorMocks.getError('ERR_NETWORK');
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(result?.message).toBe(error.message);
|
||||
expect(result?.code).toBe(MessageImportDriverExceptionCode.TEMPORARY_ERROR);
|
||||
});
|
||||
|
||||
it('should return undefined for unknown error codes', () => {
|
||||
const error = { code: 'UNKNOWN_ERROR' } as any;
|
||||
const result = parseGaxiosError(error);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
@ -0,0 +1,122 @@
|
||||
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 { parseGmailMessageListFetchError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-message-list-fetch-error.util';
|
||||
|
||||
describe('parseGmailMessageListFetchError', () => {
|
||||
it('should handle 400 Bad Request', () => {
|
||||
const error = gmailApiErrorMocks.getError(400);
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(MessageImportDriverExceptionCode.UNKNOWN);
|
||||
});
|
||||
|
||||
it('should handle 400 Invalid Grant', () => {
|
||||
const error = gmailApiErrorMocks.getError(400, 'invalid_grant');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 400 Failed Precondition', () => {
|
||||
const error = gmailApiErrorMocks.getError(400, 'failedPrecondition');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 401 Invalid Credentials', () => {
|
||||
const error = gmailApiErrorMocks.getError(401);
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Daily Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'dailyLimit');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 User Rate Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'userRateLimit');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Rate Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'rateLimit');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Domain Policy Error', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'domainPolicy');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 404 Not Found', () => {
|
||||
const error = gmailApiErrorMocks.getError(404);
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(MessageImportDriverExceptionCode.NOT_FOUND);
|
||||
});
|
||||
|
||||
it('should handle 410 Gone', () => {
|
||||
const error = gmailApiErrorMocks.getError(410);
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(MessageImportDriverExceptionCode.UNKNOWN);
|
||||
});
|
||||
|
||||
it('should handle 429 Too Many Requests', () => {
|
||||
const error = gmailApiErrorMocks.getError(429, 'concurrent');
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 500 Backend Error', () => {
|
||||
const error = gmailApiErrorMocks.getError(500);
|
||||
const exception = parseGmailMessageListFetchError(error.error);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,185 @@
|
||||
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';
|
||||
|
||||
const messageExternalId = '123';
|
||||
|
||||
describe('parseGmailMessagesImportError', () => {
|
||||
it('should handle 400 Bad Request', () => {
|
||||
const error = gmailApiErrorMocks.getError(400);
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 400 Invalid Grant', () => {
|
||||
const error = gmailApiErrorMocks.getError(400, 'invalid_grant');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 400 Failed Precondition', () => {
|
||||
const error = gmailApiErrorMocks.getError(400, 'failedPrecondition');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.error,
|
||||
messageExternalId,
|
||||
);
|
||||
|
||||
expect(exception).toBeInstanceOf(MessageImportDriverException);
|
||||
expect(exception?.code).toBe(
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 401 Invalid Credentials', () => {
|
||||
const error = gmailApiErrorMocks.getError(401);
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Daily Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'dailyLimit');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 User Rate Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'userRateLimit');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Rate Limit Exceeded', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'rateLimit');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 403 Domain Policy Error', () => {
|
||||
const error = gmailApiErrorMocks.getError(403, 'domainPolicy');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 404 Not Found', () => {
|
||||
const error = gmailApiErrorMocks.getError(404);
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.error,
|
||||
messageExternalId,
|
||||
);
|
||||
|
||||
expect(exception).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle 410 Gone', () => {
|
||||
const error = gmailApiErrorMocks.getError(410);
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.error,
|
||||
messageExternalId,
|
||||
);
|
||||
|
||||
expect(exception).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle 429 Too Many Requests', () => {
|
||||
const error = gmailApiErrorMocks.getError(429, 'concurrent');
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 500 Backend Error', () => {
|
||||
const error = gmailApiErrorMocks.getError(500);
|
||||
const exception = parseGmailMessagesImportError(
|
||||
error.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}`,
|
||||
);
|
||||
});
|
||||
});
|
@ -7,7 +7,7 @@ import {
|
||||
|
||||
export const parseGaxiosError = (
|
||||
error: GaxiosError,
|
||||
): MessageImportDriverException => {
|
||||
): MessageImportDriverException | undefined => {
|
||||
const { code } = error;
|
||||
|
||||
switch (code) {
|
||||
@ -22,9 +22,6 @@ export const parseGaxiosError = (
|
||||
);
|
||||
|
||||
default:
|
||||
return new MessageImportDriverException(
|
||||
error.message,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
@ -3,12 +3,17 @@ import {
|
||||
MessageImportDriverExceptionCode,
|
||||
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
|
||||
|
||||
export const parseGmailError = (error: {
|
||||
export const parseGmailMessageListFetchError = (error: {
|
||||
code?: number;
|
||||
reason: string;
|
||||
message: string;
|
||||
errors: {
|
||||
reason: string;
|
||||
message: string;
|
||||
}[];
|
||||
}): MessageImportDriverException => {
|
||||
const { code, reason, message } = error;
|
||||
const { code, errors } = error;
|
||||
|
||||
const reason = errors?.[0]?.reason;
|
||||
const message = errors?.[0]?.message;
|
||||
|
||||
switch (code) {
|
||||
case 400:
|
||||
@ -45,19 +50,23 @@ export const parseGmailError = (error: {
|
||||
case 403:
|
||||
if (
|
||||
reason === 'rateLimitExceeded' ||
|
||||
reason === 'userRateLimitExceeded'
|
||||
reason === 'userRateLimitExceeded' ||
|
||||
reason === 'dailyLimitExceeded'
|
||||
) {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
} else {
|
||||
}
|
||||
if (reason === 'domainPolicy') {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 401:
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
@ -70,13 +79,10 @@ export const parseGmailError = (error: {
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
} else {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import {
|
||||
MessageImportDriverException,
|
||||
MessageImportDriverExceptionCode,
|
||||
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
|
||||
|
||||
export const parseGmailMessagesImportError = (
|
||||
error: {
|
||||
code?: number;
|
||||
errors: {
|
||||
reason: string;
|
||||
message: string;
|
||||
}[];
|
||||
},
|
||||
messageExternalId: string,
|
||||
): MessageImportDriverException | undefined => {
|
||||
const { code, errors } = error;
|
||||
|
||||
const reason = errors?.[0]?.reason;
|
||||
const message = `${errors?.[0]?.message} for message with externalId: ${messageExternalId}`;
|
||||
|
||||
switch (code) {
|
||||
case 400:
|
||||
if (reason === 'invalid_grant') {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
if (reason === 'failedPrecondition') {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
|
||||
case 404:
|
||||
case 410:
|
||||
return undefined;
|
||||
|
||||
case 429:
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
|
||||
case 403:
|
||||
if (
|
||||
reason === 'rateLimitExceeded' ||
|
||||
reason === 'userRateLimitExceeded' ||
|
||||
reason === 'dailyLimitExceeded'
|
||||
) {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
}
|
||||
if (reason === 'domainPolicy') {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 401:
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
|
||||
case 500:
|
||||
if (reason === 'backendError') {
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new MessageImportDriverException(
|
||||
message,
|
||||
MessageImportDriverExceptionCode.UNKNOWN,
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user