[Messaging] Delete empty threads after message deletion import (#3716)
* [Messaging] Delete empty threads after message deletion import * fix
This commit is contained in:
@ -4,6 +4,7 @@ import { EnvironmentModule } from 'src/integrations/environment/environment.modu
|
||||
import { ConnectedAccountModule } from 'src/workspace/messaging/connected-account/connected-account.module';
|
||||
import { MessageChannelMessageAssociationModule } from 'src/workspace/messaging/message-channel-message-association/message-channel-message-assocation.module';
|
||||
import { MessageChannelModule } from 'src/workspace/messaging/message-channel/message-channel.module';
|
||||
import { MessageThreadModule } from 'src/workspace/messaging/message-thread/message-thread.module';
|
||||
import { MessageModule } from 'src/workspace/messaging/message/message.module';
|
||||
import { GmailClientProvider } from 'src/workspace/messaging/providers/gmail/gmail-client.provider';
|
||||
import { FetchMessagesByBatchesService } from 'src/workspace/messaging/services/fetch-messages-by-batches.service';
|
||||
@ -21,6 +22,7 @@ import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/wo
|
||||
MessageChannelModule,
|
||||
MessageChannelMessageAssociationModule,
|
||||
MessageModule,
|
||||
MessageThreadModule,
|
||||
],
|
||||
providers: [
|
||||
GmailFullSyncService,
|
||||
|
||||
@ -52,8 +52,8 @@ export class GmailFullSyncService {
|
||||
|
||||
const gmailMessageChannel =
|
||||
await this.messageChannelService.getFirstByConnectedAccountIdOrFail(
|
||||
workspaceId,
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const gmailMessageChannelId = gmailMessageChannel.id;
|
||||
@ -129,7 +129,7 @@ export class GmailFullSyncService {
|
||||
|
||||
if (!historyId) throw new Error('No history id found');
|
||||
|
||||
await this.connectedAccountService.saveLastSyncHistoryId(
|
||||
await this.connectedAccountService.updateLastSyncHistoryId(
|
||||
historyId,
|
||||
connectedAccount.id,
|
||||
workspaceId,
|
||||
|
||||
@ -82,8 +82,8 @@ export class GmailPartialSyncService {
|
||||
|
||||
const gmailMessageChannel =
|
||||
await this.messageChannelService.getFirstByConnectedAccountIdOrFail(
|
||||
workspaceId,
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const gmailMessageChannelId = gmailMessageChannel.id;
|
||||
@ -100,24 +100,29 @@ export class GmailPartialSyncService {
|
||||
accessToken,
|
||||
);
|
||||
|
||||
await this.utils.saveMessages(
|
||||
messagesToSave,
|
||||
dataSourceMetadata,
|
||||
workspaceDataSource,
|
||||
connectedAccount,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
if (messagesToSave.length !== 0) {
|
||||
await this.utils.saveMessages(
|
||||
messagesToSave,
|
||||
dataSourceMetadata,
|
||||
workspaceDataSource,
|
||||
connectedAccount,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
await this.utils.deleteMessages(
|
||||
messagesDeleted,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
if (messagesDeleted.length !== 0) {
|
||||
await this.utils.deleteMessages(
|
||||
workspaceDataSource,
|
||||
messagesDeleted,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
if (errors.length) throw new Error('Error fetching messages');
|
||||
|
||||
await this.connectedAccountService.saveLastSyncHistoryId(
|
||||
await this.connectedAccountService.updateLastSyncHistoryId(
|
||||
newHistoryId,
|
||||
connectedAccount.id,
|
||||
workspaceId,
|
||||
|
||||
@ -3,38 +3,25 @@ import { Injectable } from '@nestjs/common';
|
||||
import axios from 'axios';
|
||||
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||
import { ConnectedAccountService } from 'src/workspace/messaging/connected-account/connected-account.service';
|
||||
|
||||
@Injectable()
|
||||
export class GmailRefreshAccessTokenService {
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly connectedAccountService: ConnectedAccountService,
|
||||
) {}
|
||||
|
||||
async refreshAndSaveAccessToken(
|
||||
workspaceId: string,
|
||||
connectedAccountId: string,
|
||||
): Promise<void> {
|
||||
const { dataSource: workspaceDataSource, dataSourceMetadata } =
|
||||
await this.workspaceDataSourceService.connectedToWorkspaceDataSourceAndReturnMetadata(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!workspaceDataSource) {
|
||||
throw new Error('No workspace data source found');
|
||||
}
|
||||
|
||||
const connectedAccounts = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'google' AND "id" = $1`,
|
||||
[connectedAccountId],
|
||||
const connectedAccount = await this.connectedAccountService.getByIdOrFail(
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!connectedAccounts || connectedAccounts.length === 0) {
|
||||
throw new Error('No connected account found');
|
||||
}
|
||||
|
||||
const refreshToken = connectedAccounts[0]?.refreshToken;
|
||||
const refreshToken = connectedAccount.refreshToken;
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new Error('No refresh token found');
|
||||
@ -42,9 +29,10 @@ export class GmailRefreshAccessTokenService {
|
||||
|
||||
const accessToken = await this.refreshAccessToken(refreshToken);
|
||||
|
||||
await workspaceDataSource?.query(
|
||||
`UPDATE ${dataSourceMetadata.schema}."connectedAccount" SET "accessToken" = $1 WHERE "id" = $2`,
|
||||
[accessToken, connectedAccounts[0].id],
|
||||
await this.connectedAccountService.updateAccessToken(
|
||||
accessToken,
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -11,12 +11,16 @@ import {
|
||||
import { MessageQuery } from 'src/workspace/messaging/types/message-or-thread-query';
|
||||
import { MessageChannelMessageAssociationService } from 'src/workspace/messaging/message-channel-message-association/message-channel-message-association.service';
|
||||
import { MessageService } from 'src/workspace/messaging/message/message.service';
|
||||
import { MessageThreadService } from 'src/workspace/messaging/message-thread/message-thread.service';
|
||||
import { ObjectRecord } from 'src/workspace/workspace-sync-metadata/types/object-record';
|
||||
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
||||
|
||||
@Injectable()
|
||||
export class MessagingUtilsService {
|
||||
constructor(
|
||||
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationService,
|
||||
private readonly messageService: MessageService,
|
||||
private readonly messageThreadService: MessageThreadService,
|
||||
) {}
|
||||
|
||||
public createQueriesFromMessageIds(
|
||||
@ -31,17 +35,18 @@ export class MessagingUtilsService {
|
||||
messages: GmailMessage[],
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
workspaceDataSource: DataSource,
|
||||
connectedAccount,
|
||||
connectedAccount: ObjectRecord<ConnectedAccountObjectMetadata>,
|
||||
gmailMessageChannelId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
for (const message of messages) {
|
||||
await workspaceDataSource?.transaction(async (manager) => {
|
||||
await workspaceDataSource?.transaction(async (manager: EntityManager) => {
|
||||
const existingMessageChannelMessageAssociationsCount =
|
||||
await this.messageChannelMessageAssociationService.countByMessageExternalIdsAndMessageChannelId(
|
||||
[message.externalId],
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
if (existingMessageChannelMessageAssociationsCount > 0) {
|
||||
@ -52,8 +57,8 @@ export class MessagingUtilsService {
|
||||
await this.saveMessageThreadOrReturnExistingMessageThread(
|
||||
message.messageThreadExternalId,
|
||||
dataSourceMetadata,
|
||||
manager,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const savedOrExistingMessageId =
|
||||
@ -62,8 +67,8 @@ export class MessagingUtilsService {
|
||||
savedOrExistingMessageThreadId,
|
||||
connectedAccount,
|
||||
dataSourceMetadata,
|
||||
manager,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
await manager.query(
|
||||
@ -83,14 +88,14 @@ export class MessagingUtilsService {
|
||||
private async saveMessageOrReturnExistingMessage(
|
||||
message: GmailMessage,
|
||||
messageThreadId: string,
|
||||
connectedAccount,
|
||||
connectedAccount: ObjectRecord<ConnectedAccountObjectMetadata>,
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
manager: EntityManager,
|
||||
workspaceId: string,
|
||||
manager: EntityManager,
|
||||
): Promise<string> {
|
||||
const existingMessage = await this.messageService.getFirstByHeaderMessageId(
|
||||
workspaceId,
|
||||
message.headerMessageId,
|
||||
workspaceId,
|
||||
);
|
||||
const existingMessageId = existingMessage?.id;
|
||||
|
||||
@ -132,13 +137,14 @@ export class MessagingUtilsService {
|
||||
private async saveMessageThreadOrReturnExistingMessageThread(
|
||||
messageThreadExternalId: string,
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
manager: EntityManager,
|
||||
workspaceId: string,
|
||||
manager: EntityManager,
|
||||
) {
|
||||
const existingMessageChannelMessageAssociationByMessageThreadExternalId =
|
||||
await this.messageChannelMessageAssociationService.getFirstByMessageThreadExternalId(
|
||||
messageThreadExternalId,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const existingMessageThread =
|
||||
@ -199,49 +205,91 @@ export class MessagingUtilsService {
|
||||
}
|
||||
|
||||
public async deleteMessages(
|
||||
messagesDeleted: string[],
|
||||
workspaceDataSource: DataSource,
|
||||
messagesDeletedMessageExternalIds: string[],
|
||||
gmailMessageChannelId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const messageChannelMessageAssociationsToDelete =
|
||||
await this.messageChannelMessageAssociationService.getByMessageExternalIdsAndMessageChannelId(
|
||||
messagesDeleted,
|
||||
gmailMessageChannelId,
|
||||
await workspaceDataSource?.transaction(async (manager: EntityManager) => {
|
||||
const messageChannelMessageAssociationsToDelete =
|
||||
await this.messageChannelMessageAssociationService.getByMessageExternalIdsAndMessageChannelId(
|
||||
messagesDeletedMessageExternalIds,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const messageChannelMessageAssociationIdsToDeleteIds =
|
||||
messageChannelMessageAssociationsToDelete.map(
|
||||
(messageChannelMessageAssociationToDelete) =>
|
||||
messageChannelMessageAssociationToDelete.id,
|
||||
);
|
||||
|
||||
await this.messageChannelMessageAssociationService.deleteByIds(
|
||||
messageChannelMessageAssociationIdsToDeleteIds,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const messageIdsFromMessageChannelMessageAssociationsToDelete =
|
||||
messageChannelMessageAssociationsToDelete.map(
|
||||
(messageChannelMessageAssociationToDelete) =>
|
||||
messageChannelMessageAssociationToDelete.messageId,
|
||||
);
|
||||
const messageIdsFromMessageChannelMessageAssociationsToDelete =
|
||||
messageChannelMessageAssociationsToDelete.map(
|
||||
(messageChannelMessageAssociationToDelete) =>
|
||||
messageChannelMessageAssociationToDelete.messageId,
|
||||
);
|
||||
|
||||
await this.messageChannelMessageAssociationService.deleteByMessageExternalIdsAndMessageChannelId(
|
||||
messagesDeleted,
|
||||
gmailMessageChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
const messageChannelMessageAssociationByMessageIds =
|
||||
await this.messageChannelMessageAssociationService.getByMessageIds(
|
||||
messageIdsFromMessageChannelMessageAssociationsToDelete,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const messageChannelMessageAssociationByMessageIds =
|
||||
await this.messageChannelMessageAssociationService.getByMessageIds(
|
||||
messageIdsFromMessageChannelMessageAssociationsToDelete,
|
||||
const messageIdsFromMessageChannelMessageAssociationByMessageIds =
|
||||
messageChannelMessageAssociationByMessageIds.map(
|
||||
(messageChannelMessageAssociation) =>
|
||||
messageChannelMessageAssociation.messageId,
|
||||
);
|
||||
|
||||
const messageIdsToDelete =
|
||||
messageIdsFromMessageChannelMessageAssociationsToDelete.filter(
|
||||
(messageId) =>
|
||||
!messageIdsFromMessageChannelMessageAssociationByMessageIds.includes(
|
||||
messageId,
|
||||
),
|
||||
);
|
||||
|
||||
await this.messageService.deleteByIds(
|
||||
messageIdsToDelete,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
const messageIdsFromMessageChannelMessageAssociationByMessageIds =
|
||||
messageChannelMessageAssociationByMessageIds.map(
|
||||
(messageChannelMessageAssociation) =>
|
||||
messageChannelMessageAssociation.messageId,
|
||||
);
|
||||
const messageThreadIdsFromMessageChannelMessageAssociationsToDelete =
|
||||
messageChannelMessageAssociationsToDelete.map(
|
||||
(messageChannelMessageAssociationToDelete) =>
|
||||
messageChannelMessageAssociationToDelete.messageThreadId,
|
||||
);
|
||||
|
||||
const messageIdsToDelete =
|
||||
messageIdsFromMessageChannelMessageAssociationsToDelete.filter(
|
||||
(messageId) =>
|
||||
!messageIdsFromMessageChannelMessageAssociationByMessageIds.includes(
|
||||
messageId,
|
||||
),
|
||||
);
|
||||
const messagesByThreadIds =
|
||||
await this.messageService.getByMessageThreadIds(
|
||||
messageThreadIdsFromMessageChannelMessageAssociationsToDelete,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
|
||||
await this.messageService.deleteByIds(workspaceId, messageIdsToDelete);
|
||||
const threadIdsToDelete =
|
||||
messageThreadIdsFromMessageChannelMessageAssociationsToDelete.filter(
|
||||
(threadId) =>
|
||||
!messagesByThreadIds.find(
|
||||
(message) => message.messageThreadId === threadId,
|
||||
),
|
||||
);
|
||||
|
||||
await this.messageThreadService.deleteByIds(
|
||||
threadIdsToDelete,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user