Skip to content

Commit

Permalink
8733 refactor gmailhandleerrorservice (#8901)
Browse files Browse the repository at this point in the history
Closes #8733 
- Refactor `GmailHandleErrorService`
- Add tests and mocks for the errors
  • Loading branch information
bosiraphael authored Dec 5, 2024
1 parent 680366e commit de56c01
Show file tree
Hide file tree
Showing 13 changed files with 878 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 {
Expand All @@ -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,
) {}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class GmailGetHistoryService {
labelId,
})
.catch((error) => {
this.gmailHandleErrorService.handleError(error);
this.gmailHandleErrorService.handleGmailMessageListFetchError(error);

return {
data: {
Expand Down
Loading

0 comments on commit de56c01

Please sign in to comment.