5748 Create contacts for emails sent and received by email aliases (#5855)

Closes #5748
- Create feature flag
- Add scope `https://www.googleapis.com/auth/profile.emails.read` when
connecting an account
- Get email aliases with google people API, store them in
connectedAccount and refresh them before each message-import
- Update the contact creation logic accordingly
- Refactor

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
bosiraphael
2024-07-01 14:21:34 +02:00
committed by GitHub
parent a15884ea0a
commit 8c33d91734
52 changed files with 1143 additions and 754 deletions

View File

@ -2,8 +2,11 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { EmailAliasManagerModule } from 'src/modules/connected-account/email-alias-manager/email-alias-manager.module';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
import { GoogleAPIRefreshAccessTokenModule } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.module';
import { BlocklistWorkspaceEntity } from 'src/modules/connected-account/standard-objects/blocklist.workspace-entity';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@ -30,6 +33,9 @@ import { MessagingGmailPartialMessageListFetchService } from 'src/modules/messag
]),
MessagingCommonModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
OAuth2ClientManagerModule,
EmailAliasManagerModule,
FeatureFlagModule,
],
providers: [
MessagingGmailClientProvider,

View File

@ -1,16 +1,21 @@
import { Injectable } from '@nestjs/common';
import { OAuth2Client } from 'google-auth-library';
import { gmail_v1, google } from 'googleapis';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@Injectable()
export class MessagingGmailClientProvider {
constructor(private readonly environmentService: EnvironmentService) {}
constructor(
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
) {}
public async getGmailClient(refreshToken: string): Promise<gmail_v1.Gmail> {
const oAuth2Client = await this.getOAuth2Client(refreshToken);
public async getGmailClient(
connectedAccount: ConnectedAccountWorkspaceEntity,
): Promise<gmail_v1.Gmail> {
const oAuth2Client =
await this.oAuth2ClientManagerService.getOAuth2Client(connectedAccount);
const gmailClient = google.gmail({
version: 'v1',
@ -19,22 +24,4 @@ export class MessagingGmailClientProvider {
return gmailClient;
}
private async getOAuth2Client(refreshToken: string): Promise<OAuth2Client> {
const gmailClientId = this.environmentService.get('AUTH_GOOGLE_CLIENT_ID');
const gmailClientSecret = this.environmentService.get(
'AUTH_GOOGLE_CLIENT_SECRET',
);
const oAuth2Client = new google.auth.OAuth2(
gmailClientId,
gmailClientSecret,
);
oAuth2Client.setCredentials({
refresh_token: refreshToken,
});
return oAuth2Client;
}
}

View File

@ -54,9 +54,7 @@ export class MessagingGmailFullMessageListFetchService {
);
const gmailClient: gmail_v1.Gmail =
await this.gmailClientProvider.getGmailClient(
connectedAccount.refreshToken,
);
await this.gmailClientProvider.getGmailClient(connectedAccount);
const { error: gmailError } =
await this.fetchAllMessageIdsFromGmailAndStoreInCache(

View File

@ -20,6 +20,9 @@ import { MessagingGmailFetchMessagesByBatchesService } from 'src/modules/messagi
import { MessagingErrorHandlingService } from 'src/modules/messaging/common/services/messaging-error-handling.service';
import { MessagingSaveMessagesAndEnqueueContactCreationService } from 'src/modules/messaging/common/services/messaging-save-messages-and-enqueue-contact-creation.service';
import { MessageChannelRepository } from 'src/modules/messaging/common/repositories/message-channel.repository';
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
import { IsFeatureEnabledService } from 'src/engine/core-modules/feature-flag/services/is-feature-enabled.service';
import { FeatureFlagKeys } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
@Injectable()
@ -41,6 +44,8 @@ export class MessagingGmailMessagesImportService {
private readonly blocklistRepository: BlocklistRepository,
@InjectObjectMetadataRepository(MessageChannelWorkspaceEntity)
private readonly messageChannelRepository: MessageChannelRepository,
private readonly emailAliasManagerService: EmailAliasManagerService,
private readonly isFeatureEnabledService: IsFeatureEnabledService,
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
private readonly connectedAccountRepository: ConnectedAccountRepository,
) {}
@ -78,8 +83,8 @@ export class MessagingGmailMessagesImportService {
try {
accessToken =
await this.googleAPIsRefreshAccessTokenService.refreshAndSaveAccessToken(
connectedAccount,
workspaceId,
connectedAccount.id,
);
} catch (error) {
await this.messagingTelemetryService.track({
@ -103,6 +108,30 @@ export class MessagingGmailMessagesImportService {
return;
}
if (
await this.isFeatureEnabledService.isFeatureEnabled(
FeatureFlagKeys.IsMessagingAliasFetchingEnabled,
workspaceId,
)
) {
try {
await this.emailAliasManagerService.refreshEmailAliases(
connectedAccount,
workspaceId,
);
} catch (error) {
await this.gmailErrorHandlingService.handleGmailError(
{
code: error.code,
reason: error.message,
},
'messages-import',
messageChannel,
workspaceId,
);
}
}
const messageIdsToFetch =
(await this.cacheStorage.setPop(
`messages-to-import:${workspaceId}:gmail:${messageChannel.id}`,

View File

@ -52,9 +52,7 @@ export class MessagingGmailPartialMessageListFetchService {
const lastSyncHistoryId = messageChannel.syncCursor;
const gmailClient: gmail_v1.Gmail =
await this.gmailClientProvider.getGmailClient(
connectedAccount.refreshToken,
);
await this.gmailClientProvider.getGmailClient(connectedAccount);
const { history, historyId, error } =
await this.gmailGetHistoryService.getHistory(