[backend] rename repository services and replace repository modules by dynamicModule (#4536)

* rename database services to repository

* refactor more repositories

* more refactoring

* followup

* remove unused imports

* fix

* fix

* Fix calendar listener being called when flag is off

* remove folders
This commit is contained in:
Weiko 2024-03-18 16:26:23 +01:00 committed by GitHub
parent 2aa6bcdb70
commit 8fb1ab8933
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
79 changed files with 1080 additions and 776 deletions

View File

@ -11,18 +11,14 @@ import { ObjectMetadataModule } from 'src/engine-metadata/object-metadata/object
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
import { CleanInactiveWorkspaceJob } from 'src/engine/workspace-manager/workspace-cleaner/crons/clean-inactive-workspace.job';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { MessagingModule } from 'src/modules/messaging/messaging.module';
import { GmailPartialSyncJob } from 'src/modules/messaging/jobs/gmail-partial-sync.job';
import { EmailSenderJob } from 'src/engine/integrations/email/email-sender.job';
import { UserModule } from 'src/engine/modules/user/user.module';
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
import { FetchAllWorkspacesMessagesJob } from 'src/modules/messaging/commands/crons/fetch-all-workspaces-messages.job';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { MatchMessageParticipantJob } from 'src/modules/messaging/jobs/match-message-participant.job';
import { CreateCompaniesAndContactsAfterSyncJob } from 'src/modules/messaging/jobs/create-companies-and-contacts-after-sync.job';
import { CreateCompaniesAndContactsModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.module';
import { MessageChannelModule } from 'src/modules/messaging/repositories/message-channel/message-channel.module';
import { MessageParticipantModule } from 'src/modules/messaging/repositories/message-participant/message-participant.module';
import { DataSeedDemoWorkspaceModule } from 'src/database/commands/data-seed-demo-workspace/data-seed-demo-workspace.module';
import { DataSeedDemoWorkspaceJob } from 'src/database/commands/data-seed-demo-workspace/jobs/data-seed-demo-workspace.job';
import { DeleteConnectedAccountAssociatedMessagingDataJob } from 'src/modules/messaging/jobs/delete-connected-account-associated-messaging-data.job';
@ -33,27 +29,31 @@ import { UserWorkspaceModule } from 'src/engine/modules/user-workspace/user-work
import { StripeModule } from 'src/engine/modules/billing/stripe/stripe.module';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { CalendarModule } from 'src/modules/calendar/calendar.module';
import { DataSourceEntity } from 'src/engine-metadata/data-source/data-source.entity';
import { GoogleCalendarFullSyncJob } from 'src/modules/calendar/jobs/google-calendar-full-sync.job';
import { CalendarEventCleanerModule } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module';
import { RecordPositionBackfillJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job';
import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module';
import { DeleteConnectedAccountAssociatedCalendarDataJob } from 'src/modules/messaging/jobs/delete-connected-account-associated-calendar-data.job';
import { GoogleCalendarFullSyncModule } from 'src/modules/calendar/services/google-calendar-full-sync.module';
import { GoogleAPIRefreshAccessTokenModule } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.module';
import { GmailFullSyncModule } from 'src/modules/messaging/services/gmail-full-sync/gmail-full-sync.module';
import { GmailPartialSyncModule } from 'src/modules/messaging/services/gmail-partial-sync/gmail-partial-sync.module';
import { MessageParticipantModule } from 'src/modules/messaging/services/message-participant/message-participant.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
@Module({
imports: [
BillingModule,
DataSourceModule,
ConnectedAccountModule,
CreateCompaniesAndContactsModule,
DataSeedDemoWorkspaceModule,
EnvironmentModule,
HttpModule,
MessagingModule,
MessageParticipantModule,
MessageChannelModule,
CalendarModule,
GoogleCalendarFullSyncModule,
ObjectMetadataModule,
StripeModule,
ThreadCleanerModule,
@ -65,6 +65,15 @@ import { DeleteConnectedAccountAssociatedCalendarDataJob } from 'src/modules/mes
UserWorkspaceModule,
WorkspaceDataSourceModule,
RecordPositionBackfillModule,
GoogleAPIRefreshAccessTokenModule,
GmailFullSyncModule,
GmailPartialSyncModule,
MessageParticipantModule,
ObjectMetadataRepositoryModule.forFeature([
ConnectedAccountObjectMetadata,
MessageParticipantObjectMetadata,
MessageChannelObjectMetadata,
]),
],
providers: [
{

View File

@ -0,0 +1,33 @@
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
import { CalendarEventAttendeeRepository } from 'src/modules/calendar/repositories/calendar-event-attendee.repository';
import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository';
import { CompanyRepository } from 'src/modules/company/repositories/company.repository';
import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/repositories/message-channel-message-association.repository';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageParticipantRepository } from 'src/modules/messaging/repositories/message-participant.repository';
import { MessageThreadRepository } from 'src/modules/messaging/repositories/message-thread.repository';
import { MessageRepository } from 'src/modules/messaging/repositories/message.repository';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
export const metadataToRepositoryMapping = {
BlocklistObjectMetadata: BlocklistRepository,
CalendarChannelEventAssociationObjectMetadata:
CalendarChannelEventAssociationRepository,
CalendarChannelObjectMetadata: CalendarChannelRepository,
CalendarEventAttendeeObjectMetadata: CalendarEventAttendeeRepository,
CalendarEventObjectMetadata: CalendarEventRepository,
CompanyObjectMetadata: CompanyRepository,
ConnectedAccountObjectMetadata: ConnectedAccountRepository,
MessageChannelMessageAssociationObjectMetadata:
MessageChannelMessageAssociationRepository,
MessageChannelObjectMetadata: MessageChannelRepository,
MessageObjectMetadata: MessageRepository,
MessageParticipantObjectMetadata: MessageParticipantRepository,
MessageThreadObjectMetadata: MessageThreadRepository,
PersonObjectMetadata: PersonRepository,
WorkspaceMemberObjectMetadata: WorkspaceMemberRepository,
};

View File

@ -0,0 +1,12 @@
import { Inject } from '@nestjs/common';
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
import { capitalize } from 'src/utils/capitalize';
export const InjectObjectMetadataRepository = (objectMetadata: any) => {
const token = `${capitalize(
convertClassNameToObjectMetadataName(objectMetadata.name),
)}Repository`;
return Inject(token);
};

View File

@ -0,0 +1,40 @@
import { Global, Module, DynamicModule, Provider } from '@nestjs/common';
import { metadataToRepositoryMapping } from 'src/engine/object-metadata-repository/metadata-to-repository.mapping';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
import { capitalize } from 'src/utils/capitalize';
@Global()
@Module({})
export class ObjectMetadataRepositoryModule {
static forFeature(objectMetadatas): DynamicModule {
const providers: Provider[] = objectMetadatas.map((objectMetadata) => {
const repositoryClass = metadataToRepositoryMapping[objectMetadata.name];
if (!repositoryClass) {
throw new Error(`No repository found for ${objectMetadata.name}`);
}
return {
provide: `${capitalize(
convertClassNameToObjectMetadataName(objectMetadata.name),
)}Repository`,
useFactory: (
workspaceDataSourceService: WorkspaceDataSourceService,
) => {
return new repositoryClass(workspaceDataSourceService);
},
inject: [WorkspaceDataSourceService],
};
});
return {
module: ObjectMetadataRepositoryModule,
imports: [WorkspaceDataSourceModule],
providers: [...providers],
exports: providers,
};
}
}

View File

@ -1,41 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
import { CreateCompaniesAndContactsModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.module';
import { BlocklistModule } from 'src/modules/connected-account/repositories/blocklist/blocklist.module';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { CalendarChannelEventAssociationModule } from 'src/modules/calendar/repositories/calendar-channel-event-association/calendar-channel-event-assocation.module';
import { CalendarChannelModule } from 'src/modules/calendar/repositories/calendar-channel/calendar-channel.module';
import { CalendarEventAttendeeModule } from 'src/modules/calendar/repositories/calendar-event-attendee/calendar-event-attendee.module';
import { CalendarEventModule } from 'src/modules/calendar/repositories/calendar-event/calendar-event.module';
import { CalendarEventCleanerModule } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module';
import { GoogleCalendarFullSyncService } from 'src/modules/calendar/services/google-calendar-full-sync.service';
import { GoogleCalendarClientProvider } from 'src/modules/calendar/services/providers/google-calendar/google-calendar.provider';
import { CompanyModule } from 'src/modules/messaging/repositories/company/company.module';
import { PersonModule } from 'src/modules/person/repositories/person/person.module';
import { WorkspaceMemberModule } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [
EnvironmentModule,
WorkspaceDataSourceModule,
ConnectedAccountModule,
CalendarChannelModule,
CalendarChannelEventAssociationModule,
CalendarEventModule,
CalendarEventAttendeeModule,
CreateCompaniesAndContactsModule,
WorkspaceMemberModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
CompanyModule,
PersonModule,
BlocklistModule,
CalendarEventCleanerModule,
],
providers: [GoogleCalendarFullSyncService, GoogleCalendarClientProvider],
exports: [GoogleCalendarFullSyncService],
imports: [],
providers: [],
exports: [],
})
export class CalendarModule {}

View File

@ -4,11 +4,13 @@ import { Command, CommandRunner, Option } from 'nest-commander';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import {
GoogleCalendarFullSyncJobData,
GoogleCalendarFullSyncJob,
} from 'src/modules/calendar/jobs/google-calendar-full-sync.job';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
interface GoogleCalendarFullSyncOptions {
workspaceId: string;
@ -23,7 +25,8 @@ export class GoogleCalendarFullSyncCommand extends CommandRunner {
constructor(
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
) {
super();
}
@ -48,7 +51,7 @@ export class GoogleCalendarFullSyncCommand extends CommandRunner {
private async fetchWorkspaceCalendars(workspaceId: string): Promise<void> {
const connectedAccounts =
await this.connectedAccountService.getAll(workspaceId);
await this.connectedAccountRepository.getAll(workspaceId);
for (const connectedAccount of connectedAccounts) {
await this.messageQueueService.add<GoogleCalendarFullSyncJobData>(

View File

@ -1,18 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { GoogleCalendarFullSyncCommand } from 'src/modules/calendar/commands/google-calendar-full-sync.command';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
@Module({
imports: [
DataSourceModule,
TypeORMModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
ConnectedAccountModule,
ObjectMetadataRepositoryModule.forFeature([ConnectedAccountObjectMetadata]),
],
providers: [GoogleCalendarFullSyncCommand],
})

View File

@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { GoogleAPIsRefreshAccessTokenService } from 'src/modules/connected-account/services/google-apis-refresh-access-token.service';
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
import { GoogleCalendarFullSyncService } from 'src/modules/calendar/services/google-calendar-full-sync.service';
export type GoogleCalendarFullSyncJobData = {
@ -18,7 +18,7 @@ export class GoogleCalendarFullSyncJob
private readonly logger = new Logger(GoogleCalendarFullSyncJob.name);
constructor(
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIsRefreshAccessTokenService,
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService,
private readonly googleCalendarFullSyncService: GoogleCalendarFullSyncService,
) {}

View File

@ -8,7 +8,7 @@ import { CalendarChannelEventAssociationObjectMetadata } from 'src/modules/calen
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/getFlattenedValuesAndValuesStringForBatchRawQuery.util';
@Injectable()
export class CalendarChannelEventAssociationService {
export class CalendarChannelEventAssociationRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { CalendarChannelEventAssociationService } from 'src/modules/calendar/repositories/calendar-channel-event-association/calendar-channel-event-association.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [CalendarChannelEventAssociationService],
exports: [CalendarChannelEventAssociationService],
})
export class CalendarChannelEventAssociationModule {}

View File

@ -7,7 +7,7 @@ import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-obj
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
@Injectable()
export class CalendarChannelService {
export class CalendarChannelRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { CalendarChannelService } from 'src/modules/calendar/repositories/calendar-channel/calendar-channel.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [CalendarChannelService],
exports: [CalendarChannelService],
})
export class CalendarChannelModule {}

View File

@ -9,7 +9,7 @@ import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/c
import { CalendarEventAttendee } from 'src/modules/calendar/types/calendar-event';
@Injectable()
export class CalendarEventAttendeeService {
export class CalendarEventAttendeeRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { CalendarEventAttendeeService } from 'src/modules/calendar/repositories/calendar-event-attendee/calendar-event-attendee.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [CalendarEventAttendeeService],
exports: [CalendarEventAttendeeService],
})
export class CalendarEventAttendeeModule {}

View File

@ -10,7 +10,7 @@ import { CalendarEvent } from 'src/modules/calendar/types/calendar-event';
import { CalendarEventAttendeeObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata';
@Injectable()
export class CalendarEventService {
export class CalendarEventRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { CalendarEventService } from 'src/modules/calendar/repositories/calendar-event/calendar-event.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [CalendarEventService],
exports: [CalendarEventService],
})
export class CalendarEventModule {}

View File

@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
import { CalendarEventModule } from 'src/modules/calendar/repositories/calendar-event/calendar-event.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { CalendarEventCleanerService } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service';
import { CalendarEventObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event.object-metadata';
@Module({
imports: [DataSourceModule, TypeORMModule, CalendarEventModule],
imports: [
ObjectMetadataRepositoryModule.forFeature([CalendarEventObjectMetadata]),
],
providers: [CalendarEventCleanerService],
exports: [CalendarEventCleanerService],
})

View File

@ -1,20 +1,27 @@
import { Injectable } from '@nestjs/common';
import { CalendarEventService } from 'src/modules/calendar/repositories/calendar-event/calendar-event.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository';
import { CalendarEventObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event.object-metadata';
import { deleteUsingPagination } from 'src/modules/messaging/services/thread-cleaner/utils/delete-using-pagination.util';
@Injectable()
export class CalendarEventCleanerService {
constructor(private readonly calendarEventService: CalendarEventService) {}
constructor(
@InjectObjectMetadataRepository(CalendarEventObjectMetadata)
private readonly calendarEventRepository: CalendarEventRepository,
) {}
public async cleanWorkspaceCalendarEvents(workspaceId: string) {
await deleteUsingPagination(
workspaceId,
500,
this.calendarEventService.getNonAssociatedCalendarEventIdsPaginated.bind(
this.calendarEventService,
this.calendarEventRepository.getNonAssociatedCalendarEventIdsPaginated.bind(
this.calendarEventRepository,
),
this.calendarEventRepository.deleteByIds.bind(
this.calendarEventRepository,
),
this.calendarEventService.deleteByIds.bind(this.calendarEventService),
);
}
}

View File

@ -0,0 +1,37 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { GoogleCalendarFullSyncService } from 'src/modules/calendar/services/google-calendar-full-sync.service';
import { CalendarProvidersModule } from 'src/modules/calendar/services/providers/calendar-providers.module';
import { CalendarChannelEventAssociationObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.object-metadata';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
import { CalendarEventAttendeeObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata';
import { CalendarEventObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event.object-metadata';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
@Module({
imports: [
CalendarProvidersModule,
ObjectMetadataRepositoryModule.forFeature([
ConnectedAccountObjectMetadata,
CalendarEventObjectMetadata,
CalendarChannelObjectMetadata,
CalendarChannelEventAssociationObjectMetadata,
CalendarEventAttendeeObjectMetadata,
BlocklistObjectMetadata,
PersonObjectMetadata,
WorkspaceMemberObjectMetadata,
]),
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
WorkspaceDataSourceModule,
],
providers: [GoogleCalendarFullSyncService],
exports: [GoogleCalendarFullSyncService],
})
export class GoogleCalendarFullSyncModule {}

View File

@ -3,23 +3,30 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { BlocklistService } from 'src/modules/connected-account/repositories/blocklist/blocklist.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/modules/feature-flag/feature-flag.entity';
import { GoogleCalendarClientProvider } from 'src/modules/calendar/services/providers/google-calendar/google-calendar.provider';
import { googleCalendarSearchFilterExcludeEmails } from 'src/modules/calendar/utils/google-calendar-search-filter.util';
import { CalendarChannelEventAssociationService } from 'src/modules/calendar/repositories/calendar-channel-event-association/calendar-channel-event-association.service';
import { CalendarChannelService } from 'src/modules/calendar/repositories/calendar-channel/calendar-channel.service';
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { CalendarEventService } from 'src/modules/calendar/repositories/calendar-event/calendar-event.service';
import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository';
import { formatGoogleCalendarEvent } from 'src/modules/calendar/utils/format-google-calendar-event.util';
import { GoogleCalendarFullSyncJobData } from 'src/modules/calendar/jobs/google-calendar-full-sync.job';
import { CalendarEventAttendeeService } from 'src/modules/calendar/repositories/calendar-event-attendee/calendar-event-attendee.service';
import { CalendarEventAttendeeRepository } from 'src/modules/calendar/repositories/calendar-event-attendee.repository';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CalendarEventObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event.object-metadata';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
import { CalendarChannelEventAssociationObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.object-metadata';
import { CalendarEventAttendeeObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
@Injectable()
export class GoogleCalendarFullSyncService {
@ -29,12 +36,20 @@ export class GoogleCalendarFullSyncService {
private readonly googleCalendarClientProvider: GoogleCalendarClientProvider,
@Inject(MessageQueue.calendarQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
private readonly calendarEventService: CalendarEventService,
private readonly calendarChannelService: CalendarChannelService,
private readonly calendarChannelEventAssociationService: CalendarChannelEventAssociationService,
private readonly calendarEventAttendeesService: CalendarEventAttendeeService,
private readonly blocklistService: BlocklistService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(CalendarEventObjectMetadata)
private readonly calendarEventRepository: CalendarEventRepository,
@InjectObjectMetadataRepository(CalendarChannelObjectMetadata)
private readonly calendarChannelRepository: CalendarChannelRepository,
@InjectObjectMetadataRepository(
CalendarChannelEventAssociationObjectMetadata,
)
private readonly calendarChannelEventAssociationRepository: CalendarChannelEventAssociationRepository,
@InjectObjectMetadataRepository(CalendarEventAttendeeObjectMetadata)
private readonly calendarEventAttendeesRepository: CalendarEventAttendeeRepository,
@InjectObjectMetadataRepository(BlocklistObjectMetadata)
private readonly blocklistRepository: BlocklistRepository,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@ -45,7 +60,7 @@ export class GoogleCalendarFullSyncService {
connectedAccountId: string,
pageToken?: string,
): Promise<void> {
const connectedAccount = await this.connectedAccountService.getById(
const connectedAccount = await this.connectedAccountRepository.getById(
connectedAccountId,
workspaceId,
);
@ -64,7 +79,7 @@ export class GoogleCalendarFullSyncService {
}
const calendarChannel =
await this.calendarChannelService.getFirstByConnectedAccountIdOrFail(
await this.calendarChannelRepository.getFirstByConnectedAccountIdOrFail(
connectedAccountId,
workspaceId,
);
@ -87,7 +102,7 @@ export class GoogleCalendarFullSyncService {
isBlocklistEnabledFeatureFlag && isBlocklistEnabledFeatureFlag.value;
const blocklist = isBlocklistEnabled
? await this.blocklistService.getByWorkspaceMemberId(
? await this.blocklistRepository.getByWorkspaceMemberId(
workspaceMemberId,
workspaceId,
)
@ -130,7 +145,7 @@ export class GoogleCalendarFullSyncService {
startTime = Date.now();
const existingCalendarChannelEventAssociations =
await this.calendarChannelEventAssociationService.getByEventExternalIdsAndCalendarChannelId(
await this.calendarChannelEventAssociationRepository.getByEventExternalIdsAndCalendarChannelId(
eventExternalIds,
calendarChannelId,
workspaceId,
@ -180,7 +195,7 @@ export class GoogleCalendarFullSyncService {
);
const iCalUIDCalendarEventIdMap =
await this.calendarEventService.getICalUIDCalendarEventIdMap(
await this.calendarEventRepository.getICalUIDCalendarEventIdMap(
eventsToUpdate.map((event) => event.iCalUID),
workspaceId,
);
@ -192,31 +207,31 @@ export class GoogleCalendarFullSyncService {
);
dataSourceMetadata?.transaction(async (transactionManager) => {
this.calendarEventService.saveCalendarEvents(
this.calendarEventRepository.saveCalendarEvents(
eventsToSave,
workspaceId,
transactionManager,
);
this.calendarEventService.updateCalendarEvents(
this.calendarEventRepository.updateCalendarEvents(
eventsToUpdate,
workspaceId,
transactionManager,
);
this.calendarChannelEventAssociationService.saveCalendarChannelEventAssociations(
this.calendarChannelEventAssociationRepository.saveCalendarChannelEventAssociations(
calendarChannelEventAssociationsToSave,
workspaceId,
transactionManager,
);
this.calendarEventAttendeesService.saveCalendarEventAttendees(
this.calendarEventAttendeesRepository.saveCalendarEventAttendees(
attendeesToSave,
workspaceId,
transactionManager,
);
this.calendarEventAttendeesService.updateCalendarEventAttendees(
this.calendarEventAttendeesRepository.updateCalendarEventAttendees(
attendeesToUpdate,
iCalUIDCalendarEventIdMap,
workspaceId,

View File

@ -11,9 +11,8 @@ export type CompanyToCreate = {
city?: string;
};
// TODO: Move outside of the messaging module
@Injectable()
export class CompanyService {
export class CompanyRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,19 +1,20 @@
import { Module } from '@nestjs/common';
import { PersonModule } from 'src/modules/person/repositories/person/person.module';
import { WorkspaceMemberModule } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.module';
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.service';
import { CreateCompanyModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.module';
import { CreateContactModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-contact/create-contact.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
@Module({
imports: [
WorkspaceDataSourceModule,
CreateContactModule,
CreateCompanyModule,
WorkspaceMemberModule,
PersonModule,
ObjectMetadataRepositoryModule.forFeature([
PersonObjectMetadata,
WorkspaceMemberObjectMetadata,
]),
],
providers: [CreateCompanyAndContactService],
exports: [CreateCompanyAndContactService],

View File

@ -7,19 +7,24 @@ import { Participant } from 'src/modules/messaging/types/gmail-message';
import { getDomainNameFromHandle } from 'src/modules/messaging/utils/get-domain-name-from-handle.util';
import { CreateCompanyService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.service';
import { CreateContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-contact/create-contact.service';
import { PersonService } from 'src/modules/person/repositories/person/person.service';
import { WorkspaceMemberService } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.service';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
import { getUniqueParticipantsAndHandles } from 'src/modules/messaging/utils/get-unique-participants-and-handles.util';
import { filterOutParticipantsFromCompanyOrWorkspace } from 'src/modules/messaging/utils/filter-out-participants-from-company-or-workspace.util';
import { isWorkEmail } from 'src/utils/is-work-email';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
@Injectable()
export class CreateCompanyAndContactService {
constructor(
private readonly personService: PersonService,
private readonly createContactService: CreateContactService,
private readonly createCompaniesService: CreateCompanyService,
private readonly workspaceMemberService: WorkspaceMemberService,
@InjectObjectMetadataRepository(PersonObjectMetadata)
private readonly personRepository: PersonRepository,
@InjectObjectMetadataRepository(WorkspaceMemberObjectMetadata)
private readonly workspaceMemberRepository: WorkspaceMemberRepository,
) {}
async createCompaniesAndContacts(
@ -36,7 +41,7 @@ export class CreateCompanyAndContactService {
const isContactAutoCreationForNonWorkEmailsEnabled = false;
const workspaceMembers =
await this.workspaceMemberService.getAllByWorkspaceId(
await this.workspaceMemberRepository.getAllByWorkspaceId(
workspaceId,
transactionManager,
);
@ -55,7 +60,7 @@ export class CreateCompanyAndContactService {
return;
}
const alreadyCreatedContacts = await this.personService.getByEmails(
const alreadyCreatedContacts = await this.personRepository.getByEmails(
uniqueHandles,
workspaceId,
);

View File

@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/company.object-metadata';
import { CreateCompanyService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.service';
import { CompanyModule } from 'src/modules/messaging/repositories/company/company.module';
@Module({
imports: [WorkspaceDataSourceModule, CompanyModule],
imports: [ObjectMetadataRepositoryModule.forFeature([CompanyObjectMetadata])],
providers: [CreateCompanyService],
exports: [CreateCompanyService],
})

View File

@ -4,13 +4,18 @@ import { EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import axios, { AxiosInstance } from 'axios';
import { CompanyService } from 'src/modules/messaging/repositories/company/company.service';
import { CompanyRepository } from 'src/modules/company/repositories/company.repository';
import { getCompanyNameFromDomainName } from 'src/modules/messaging/utils/get-company-name-from-domain-name.util';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/company.object-metadata';
@Injectable()
export class CreateCompanyService {
private readonly httpService: AxiosInstance;
constructor(private readonly companyService: CompanyService) {
constructor(
@InjectObjectMetadataRepository(CompanyObjectMetadata)
private readonly companyRepository: CompanyRepository,
) {
this.httpService = axios.create({
baseURL: 'https://companies.twenty.com',
});
@ -30,7 +35,7 @@ export class CreateCompanyService {
const uniqueDomainNames = [...new Set(domainNames)];
const existingCompanies =
await this.companyService.getExistingCompaniesByDomainNames(
await this.companyRepository.getExistingCompaniesByDomainNames(
uniqueDomainNames,
workspaceId,
transactionManager,
@ -80,7 +85,7 @@ export class CreateCompanyService {
const { name, city } = await this.getCompanyInfoFromDomainName(domainName);
this.companyService.createCompany(
this.companyRepository.createCompany(
workspaceId,
{
id: companyId,

View File

@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { CreateContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-contact/create-contact.service';
import { PersonModule } from 'src/modules/person/repositories/person/person.module';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
@Module({
imports: [WorkspaceDataSourceModule, PersonModule],
imports: [ObjectMetadataRepositoryModule.forFeature([PersonObjectMetadata])],
providers: [CreateContactService],
exports: [CreateContactService],
})

View File

@ -3,8 +3,10 @@ import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { PersonService } from 'src/modules/person/repositories/person/person.service';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { getFirstNameAndLastNameFromHandleAndDisplayName } from 'src/modules/messaging/utils/get-first-name-and-last-name-from-handle-and-display-name.util';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
type ContactToCreate = {
handle: string;
@ -22,7 +24,10 @@ type FormattedContactToCreate = {
@Injectable()
export class CreateContactService {
constructor(private readonly personService: PersonService) {}
constructor(
@InjectObjectMetadataRepository(PersonObjectMetadata)
private readonly personRepository: PersonRepository,
) {}
public formatContacts(
contactsToCreate: ContactToCreate[],
@ -54,7 +59,7 @@ export class CreateContactService {
const formattedContacts = this.formatContacts(contactsToCreate);
await this.personService.createPeople(
await this.personRepository.createPeople(
formattedContacts,
workspaceId,
transactionManager,

View File

@ -7,7 +7,7 @@ import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metada
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
@Injectable()
export class BlocklistService {
export class BlocklistRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { BlocklistService } from 'src/modules/connected-account/repositories/blocklist/blocklist.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [BlocklistService],
exports: [BlocklistService],
})
export class BlocklistModule {}

View File

@ -7,7 +7,7 @@ import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/st
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
@Injectable()
export class ConnectedAccountService {
export class ConnectedAccountRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [ConnectedAccountService],
exports: [ConnectedAccountService],
})
export class ConnectedAccountModule {}

View File

@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
@Module({
imports: [
ObjectMetadataRepositoryModule.forFeature([ConnectedAccountObjectMetadata]),
],
providers: [GoogleAPIRefreshAccessTokenService],
exports: [GoogleAPIRefreshAccessTokenService],
})
export class GoogleAPIRefreshAccessTokenModule {}

View File

@ -3,20 +3,23 @@ import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
@Injectable()
export class GoogleAPIsRefreshAccessTokenService {
export class GoogleAPIRefreshAccessTokenService {
constructor(
private readonly environmentService: EnvironmentService,
private readonly connectedAccountService: ConnectedAccountService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
) {}
async refreshAndSaveAccessToken(
workspaceId: string,
connectedAccountId: string,
): Promise<void> {
const connectedAccount = await this.connectedAccountService.getById(
const connectedAccount = await this.connectedAccountRepository.getById(
connectedAccountId,
workspaceId,
);
@ -37,7 +40,7 @@ export class GoogleAPIsRefreshAccessTokenService {
const accessToken = await this.refreshAccessToken(refreshToken);
await this.connectedAccountService.updateAccessToken(
await this.connectedAccountRepository.updateAccessToken(
accessToken,
connectedAccountId,
workspaceId,

View File

@ -7,13 +7,15 @@ import { MessageQueueJob } from 'src/engine/integrations/message-queue/interface
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import {
GmailPartialSyncJobData,
GmailPartialSyncJob,
} from 'src/modules/messaging/jobs/gmail-partial-sync.job';
import { DataSourceEntity } from 'src/engine-metadata/data-source/data-source.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
@Injectable()
export class FetchAllWorkspacesMessagesJob
@ -28,7 +30,8 @@ export class FetchAllWorkspacesMessagesJob
private readonly dataSourceRepository: Repository<DataSourceEntity>,
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
) {}
async handle(): Promise<void> {
@ -59,7 +62,7 @@ export class FetchAllWorkspacesMessagesJob
private async fetchWorkspaceMessages(workspaceId: string): Promise<void> {
try {
const connectedAccounts =
await this.connectedAccountService.getAll(workspaceId);
await this.connectedAccountRepository.getAll(workspaceId);
for (const connectedAccount of connectedAccounts) {
await this.messageQueueService.add<GmailPartialSyncJobData>(

View File

@ -1,21 +1,15 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { GmailFullSyncCommand } from 'src/modules/messaging/commands/gmail-full-sync.command';
import { GmailPartialSyncCommand } from 'src/modules/messaging/commands/gmail-partial-sync.command';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { StartFetchAllWorkspacesMessagesCronCommand } from 'src/modules/messaging/commands/start-fetch-all-workspaces-messages.cron.command';
import { StopFetchAllWorkspacesMessagesCronCommand } from 'src/modules/messaging/commands/stop-fetch-all-workspaces-messages.cron.command';
@Module({
imports: [
DataSourceModule,
TypeORMModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
ConnectedAccountModule,
ObjectMetadataRepositoryModule.forFeature([ConnectedAccountObjectMetadata]),
],
providers: [
GmailFullSyncCommand,

View File

@ -8,7 +8,9 @@ import {
GmailFullSyncJobData,
GmailFullSyncJob,
} from 'src/modules/messaging/jobs/gmail-full-sync.job';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
interface GmailFullSyncOptions {
workspaceId: string;
@ -22,7 +24,8 @@ export class GmailFullSyncCommand extends CommandRunner {
constructor(
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
) {
super();
}
@ -47,7 +50,7 @@ export class GmailFullSyncCommand extends CommandRunner {
private async fetchWorkspaceMessages(workspaceId: string): Promise<void> {
const connectedAccounts =
await this.connectedAccountService.getAll(workspaceId);
await this.connectedAccountRepository.getAll(workspaceId);
for (const connectedAccount of connectedAccounts) {
await this.messageQueueService.add<GmailFullSyncJobData>(

View File

@ -8,7 +8,9 @@ import {
GmailPartialSyncJob,
GmailPartialSyncJobData,
} from 'src/modules/messaging/jobs/gmail-partial-sync.job';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
interface GmailPartialSyncOptions {
workspaceId: string;
@ -22,7 +24,8 @@ export class GmailPartialSyncCommand extends CommandRunner {
constructor(
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountService: ConnectedAccountRepository,
) {
super();
}

View File

@ -2,9 +2,13 @@ import { Injectable, Logger } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.service';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { MessageParticipantService } from 'src/modules/messaging/repositories/message-participant/message-participant.service';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageParticipantRepository } from 'src/modules/messaging/repositories/message-participant.repository';
import { MessageParticipantService } from 'src/modules/messaging/services/message-participant/message-participant.service';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
export type CreateCompaniesAndContactsAfterSyncJobData = {
workspaceId: string;
@ -20,8 +24,11 @@ export class CreateCompaniesAndContactsAfterSyncJob
);
constructor(
private readonly createCompaniesAndContactsService: CreateCompanyAndContactService,
private readonly messageChannelService: MessageChannelService,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelService: MessageChannelRepository,
private readonly messageParticipantService: MessageParticipantService,
@InjectObjectMetadataRepository(MessageParticipantObjectMetadata)
private readonly messageParticipantRepository: MessageParticipantRepository,
) {}
async handle(
@ -44,7 +51,7 @@ export class CreateCompaniesAndContactsAfterSyncJob
}
const messageParticipantsWithoutPersonIdAndWorkspaceMemberId =
await this.messageParticipantService.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberIdAndMessageOutgoing(
await this.messageParticipantRepository.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberIdAndMessageOutgoing(
messageChannelId,
workspaceId,
);

View File

@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { GoogleAPIsRefreshAccessTokenService } from 'src/modules/connected-account/services/google-apis-refresh-access-token.service';
import { GmailFullSyncService } from 'src/modules/messaging/services/gmail-full-sync.service';
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
import { GmailFullSyncService } from 'src/modules/messaging/services/gmail-full-sync/gmail-full-sync.service';
export type GmailFullSyncJobData = {
workspaceId: string;
@ -16,7 +16,7 @@ export class GmailFullSyncJob implements MessageQueueJob<GmailFullSyncJobData> {
private readonly logger = new Logger(GmailFullSyncJob.name);
constructor(
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIsRefreshAccessTokenService,
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService,
private readonly gmailFullSyncService: GmailFullSyncService,
) {}

View File

@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { GoogleAPIsRefreshAccessTokenService } from 'src/modules/connected-account/services/google-apis-refresh-access-token.service';
import { GmailPartialSyncService } from 'src/modules/messaging/services/gmail-partial-sync.service';
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
import { GmailPartialSyncService } from 'src/modules/messaging/services/gmail-partial-sync/gmail-partial-sync.service';
export type GmailPartialSyncJobData = {
workspaceId: string;
@ -17,7 +17,7 @@ export class GmailPartialSyncJob
private readonly logger = new Logger(GmailPartialSyncJob.name);
constructor(
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIsRefreshAccessTokenService,
private readonly googleAPIsRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService,
private readonly gmailPartialSyncService: GmailPartialSyncService,
) {}

View File

@ -2,7 +2,9 @@ import { Injectable } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { MessageParticipantService } from 'src/modules/messaging/repositories/message-participant/message-participant.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { MessageParticipantRepository } from 'src/modules/messaging/repositories/message-participant.repository';
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
export type MatchMessageParticipantsJobData = {
workspaceId: string;
@ -16,28 +18,32 @@ export class MatchMessageParticipantJob
implements MessageQueueJob<MatchMessageParticipantsJobData>
{
constructor(
private readonly messageParticipantService: MessageParticipantService,
@InjectObjectMetadataRepository(MessageParticipantObjectMetadata)
private readonly messageParticipantRepository: MessageParticipantRepository,
) {}
async handle(data: MatchMessageParticipantsJobData): Promise<void> {
const { workspaceId, personId, workspaceMemberId, email } = data;
const messageParticipantsToUpdate =
await this.messageParticipantService.getByHandles([email], workspaceId);
await this.messageParticipantRepository.getByHandles(
[email],
workspaceId,
);
const messageParticipantIdsToUpdate = messageParticipantsToUpdate.map(
(participant) => participant.id,
);
if (personId) {
await this.messageParticipantService.updateParticipantsPersonId(
await this.messageParticipantRepository.updateParticipantsPersonId(
messageParticipantIdsToUpdate,
personId,
workspaceId,
);
}
if (workspaceMemberId) {
await this.messageParticipantService.updateParticipantsWorkspaceMemberId(
await this.messageParticipantRepository.updateParticipantsWorkspaceMemberId(
messageParticipantIdsToUpdate,
workspaceMemberId,
workspaceId,

View File

@ -1,5 +1,8 @@
import { Injectable, Inject } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
@ -13,6 +16,10 @@ import {
DeleteConnectedAccountAssociatedMessagingDataJob,
} from 'src/modules/messaging/jobs/delete-connected-account-associated-messaging-data.job';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/modules/feature-flag/feature-flag.entity';
@Injectable()
export class MessagingConnectedAccountListener {
@ -21,12 +28,20 @@ export class MessagingConnectedAccountListener {
private readonly messageQueueService: MessageQueueService,
@Inject(MessageQueue.calendarQueue)
private readonly calendarQueueService: MessageQueueService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
) {}
@OnEvent('connectedAccount.deleted')
handleDeletedEvent(
async handleDeletedEvent(
payload: ObjectRecordDeleteEvent<ConnectedAccountObjectMetadata>,
) {
const isCalendarEnabled = await this.featureFlagRepository.findOneBy({
workspaceId: payload.workspaceId,
key: FeatureFlagKeys.IsCalendarEnabled,
value: true,
});
this.messageQueueService.add<DeleteConnectedAccountAssociatedMessagingDataJobData>(
DeleteConnectedAccountAssociatedMessagingDataJob.name,
{
@ -35,6 +50,7 @@ export class MessagingConnectedAccountListener {
},
);
if (isCalendarEnabled) {
this.calendarQueueService.add<DeleteConnectedAccountAssociatedCalendarDataJobData>(
DeleteConnectedAccountAssociatedCalendarDataJob.name,
{
@ -43,4 +59,5 @@ export class MessagingConnectedAccountListener {
},
);
}
}
}

View File

@ -1,76 +1,19 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HttpModule } from '@nestjs/axios';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { MessageChannelMessageAssociationModule } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-assocation.module';
import { MessageChannelModule } from 'src/modules/messaging/repositories/message-channel/message-channel.module';
import { MessageThreadModule } from 'src/modules/messaging/repositories/message-thread/message-thread.module';
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
import { MessagingPersonListener } from 'src/modules/messaging/listeners/messaging-person.listener';
import { MessageModule } from 'src/modules/messaging/repositories/message/message.module';
import { GmailClientProvider } from 'src/modules/messaging/services/providers/gmail/gmail-client.provider';
import { CreateContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-contact/create-contact.service';
import { CreateCompanyService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.service';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches.service';
import { GmailFullSyncService } from 'src/modules/messaging/services/gmail-full-sync.service';
import { GmailPartialSyncService } from 'src/modules/messaging/services/gmail-partial-sync.service';
import { GoogleAPIsRefreshAccessTokenService } from 'src/modules/connected-account/services/google-apis-refresh-access-token.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { MessageParticipantModule } from 'src/modules/messaging/repositories/message-participant/message-participant.module';
import { MessagingWorkspaceMemberListener } from 'src/modules/messaging/listeners/messaging-workspace-member.listener';
import { MessagingMessageChannelListener } from 'src/modules/messaging/listeners/messaging-message-channel.listener';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
import { WorkspaceMemberModule } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.module';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { CreateCompaniesAndContactsModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.module';
import { CompanyModule } from 'src/modules/messaging/repositories/company/company.module';
import { PersonModule } from 'src/modules/person/repositories/person/person.module';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-messages-and-create-contacts.service';
import { MessagingConnectedAccountListener } from 'src/modules/messaging/listeners/messaging-connected-account.listener';
import { BlocklistModule } from 'src/modules/connected-account/repositories/blocklist/blocklist.module';
import { FetchByBatchesService } from 'src/modules/messaging/services/fetch-by-batch.service';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
@Module({
imports: [
EnvironmentModule,
WorkspaceDataSourceModule,
ConnectedAccountModule,
MessageChannelModule,
MessageChannelMessageAssociationModule,
MessageModule,
MessageThreadModule,
MessageParticipantModule,
CreateCompaniesAndContactsModule,
WorkspaceMemberModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
CompanyModule,
PersonModule,
BlocklistModule,
HttpModule.register({
baseURL: 'https://www.googleapis.com/batch/gmail/v1',
}),
],
imports: [TypeOrmModule.forFeature([FeatureFlagEntity], 'core')],
providers: [
GmailFullSyncService,
GmailPartialSyncService,
FetchMessagesByBatchesService,
GoogleAPIsRefreshAccessTokenService,
GmailClientProvider,
CreateContactService,
CreateCompanyService,
MessagingPersonListener,
MessagingWorkspaceMemberListener,
MessagingMessageChannelListener,
MessageService,
SaveMessagesAndCreateContactsService,
MessagingConnectedAccountListener,
FetchByBatchesService,
],
exports: [
GmailPartialSyncService,
GmailFullSyncService,
GoogleAPIsRefreshAccessTokenService,
FetchByBatchesService,
],
exports: [],
})
export class MessagingModule {}

View File

@ -10,18 +10,29 @@ import { groupBy } from 'lodash';
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
import { FindManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { MessageChannelMessageAssociationService } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-association.service';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { WorkspaceMemberService } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.service';
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/repositories/message-channel-message-association.repository';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
@Injectable()
export class MessageFindManyPreQueryHook implements WorkspacePreQueryHook {
constructor(
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationService,
private readonly messageChannelService: MessageChannelService,
private readonly connectedAccountService: ConnectedAccountService,
private readonly workspaceMemberService: WorkspaceMemberService,
@InjectObjectMetadataRepository(
MessageChannelMessageAssociationObjectMetadata,
)
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationRepository,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelService: MessageChannelRepository,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(WorkspaceMemberObjectMetadata)
private readonly workspaceMemberService: WorkspaceMemberRepository,
) {}
async execute(
@ -75,7 +86,7 @@ export class MessageFindManyPreQueryHook implements WorkspacePreQueryHook {
await this.workspaceMemberService.getByIdOrFail(userId, workspaceId);
const messageChannelsConnectedAccounts =
await this.connectedAccountService.getByIds(
await this.connectedAccountRepository.getByIds(
messageChannels.map((channel) => channel.connectedAccountId),
workspaceId,
);

View File

@ -2,17 +2,20 @@ import { Module } from '@nestjs/common';
import { MessageFindManyPreQueryHook } from 'src/modules/messaging/query-hooks/message/message-find-many.pre-query.hook';
import { MessageFindOnePreQueryHook } from 'src/modules/messaging/query-hooks/message/message-find-one.pre-query-hook';
import { ConnectedAccountModule } from 'src/modules/connected-account/repositories/connected-account/connected-account.module';
import { MessageChannelMessageAssociationModule } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-assocation.module';
import { MessageChannelModule } from 'src/modules/messaging/repositories/message-channel/message-channel.module';
import { WorkspaceMemberModule } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
@Module({
imports: [
MessageChannelMessageAssociationModule,
MessageChannelModule,
ConnectedAccountModule,
WorkspaceMemberModule,
ObjectMetadataRepositoryModule.forFeature([
MessageChannelMessageAssociationObjectMetadata,
MessageChannelObjectMetadata,
ConnectedAccountObjectMetadata,
WorkspaceMemberObjectMetadata,
]),
],
providers: [
{

View File

@ -1,12 +0,0 @@
import { Module } from '@nestjs/common';
import { CompanyService } from 'src/modules/messaging/repositories/company/company.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
// TODO: Move outside of the messaging module
@Module({
imports: [WorkspaceDataSourceModule],
providers: [CompanyService],
exports: [CompanyService],
})
export class CompanyModule {}

View File

@ -7,7 +7,7 @@ import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/mess
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
@Injectable()
export class MessageChannelMessageAssociationService {
export class MessageChannelMessageAssociationRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}
@ -192,4 +192,30 @@ export class MessageChannelMessageAssociationService {
transactionManager,
);
}
public async insert(
messageChannelId: string,
messageId: string,
messageExternalId: string,
messageThreadId: string,
messageThreadExternalId: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`INSERT INTO ${dataSourceSchema}."messageChannelMessageAssociation" ("messageChannelId", "messageId", "messageExternalId", "messageThreadId", "messageThreadExternalId") VALUES ($1, $2, $3, $4, $5)`,
[
messageChannelId,
messageId,
messageExternalId,
messageThreadId,
messageThreadExternalId,
],
workspaceId,
transactionManager,
);
}
}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { MessageChannelMessageAssociationService } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-association.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [MessageChannelMessageAssociationService],
exports: [MessageChannelMessageAssociationService],
})
export class MessageChannelMessageAssociationModule {}

View File

@ -7,7 +7,7 @@ import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-obj
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
@Injectable()
export class MessageChannelService {
export class MessageChannelRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [MessageChannelService],
exports: [MessageChannelService],
})
export class MessageChannelModule {}

View File

@ -9,13 +9,11 @@ import {
ParticipantWithId,
ParticipantWithMessageId,
} from 'src/modules/messaging/types/gmail-message';
import { PersonService } from 'src/modules/person/repositories/person/person.service';
@Injectable()
export class MessageParticipantService {
export class MessageParticipantRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly personService: PersonService,
) {}
public async getByHandles(
@ -195,45 +193,4 @@ export class MessageParticipantService {
transactionManager,
);
}
public async updateMessageParticipantsAfterPeopleCreation(
participants: ParticipantWithId[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
if (!participants) return;
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const handles = participants.map((participant) => participant.handle);
const participantPersonIds = await this.personService.getByEmails(
handles,
workspaceId,
transactionManager,
);
const messageParticipantsToUpdate = participants.map((participant) => [
participant.id,
participantPersonIds.find(
(e: { id: string; email: string }) => e.email === participant.handle,
)?.id,
]);
if (messageParticipantsToUpdate.length === 0) return;
const valuesString = messageParticipantsToUpdate
.map((_, index) => `($${index * 2 + 1}::uuid, $${index * 2 + 2}::uuid)`)
.join(', ');
await this.workspaceDataSourceService.executeRawQuery(
`UPDATE ${dataSourceSchema}."messageParticipant" AS "messageParticipant" SET "personId" = "data"."personId"
FROM (VALUES ${valuesString}) AS "data"("id", "personId")
WHERE "messageParticipant"."id" = "data"."id"`,
messageParticipantsToUpdate.flat(),
workspaceId,
transactionManager,
);
}
}

View File

@ -1,12 +0,0 @@
import { Module } from '@nestjs/common';
import { MessageParticipantService } from 'src/modules/messaging/repositories/message-participant/message-participant.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { PersonModule } from 'src/modules/person/repositories/person/person.module';
@Module({
imports: [WorkspaceDataSourceModule, PersonModule],
providers: [MessageParticipantService],
exports: [MessageParticipantService],
})
export class MessageParticipantModule {}

View File

@ -0,0 +1,67 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Injectable()
export class MessageThreadRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}
public async getOrphanThreadIdsPaginated(
limit: number,
offset: number,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<string[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const orphanThreads = await this.workspaceDataSourceService.executeRawQuery(
`SELECT mt.id
FROM ${dataSourceSchema}."messageThread" mt
LEFT JOIN ${dataSourceSchema}."message" m ON mt.id = m."messageThreadId"
WHERE m."messageThreadId" IS NULL
LIMIT $1 OFFSET $2`,
[limit, offset],
workspaceId,
transactionManager,
);
return orphanThreads.map(({ id }) => id);
}
public async deleteByIds(
messageThreadIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`DELETE FROM ${dataSourceSchema}."messageThread" WHERE id = ANY($1)`,
[messageThreadIds],
workspaceId,
transactionManager,
);
}
public async insert(
messageThreadId: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`INSERT INTO ${dataSourceSchema}."messageThread" (id) VALUES ($1)`,
[messageThreadId],
workspaceId,
transactionManager,
);
}
}

View File

@ -1,17 +0,0 @@
import { Module, forwardRef } from '@nestjs/common';
import { MessageChannelMessageAssociationModule } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-assocation.module';
import { MessageThreadService } from 'src/modules/messaging/repositories/message-thread/message-thread.service';
import { MessageModule } from 'src/modules/messaging/repositories/message/message.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [
WorkspaceDataSourceModule,
MessageChannelMessageAssociationModule,
forwardRef(() => MessageModule),
],
providers: [MessageThreadService],
exports: [MessageThreadService],
})
export class MessageThreadModule {}

View File

@ -1,105 +0,0 @@
import { Inject, Injectable, forwardRef } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { DataSourceEntity } from 'src/engine-metadata/data-source/data-source.entity';
import { MessageChannelMessageAssociationService } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-association.service';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
@Injectable()
export class MessageThreadService {
constructor(
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@Inject(forwardRef(() => MessageService))
private readonly messageService: MessageService,
) {}
public async getOrphanThreadIdsPaginated(
limit: number,
offset: number,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<string[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const orphanThreads = await this.workspaceDataSourceService.executeRawQuery(
`SELECT mt.id
FROM ${dataSourceSchema}."messageThread" mt
LEFT JOIN ${dataSourceSchema}."message" m ON mt.id = m."messageThreadId"
WHERE m."messageThreadId" IS NULL
LIMIT $1 OFFSET $2`,
[limit, offset],
workspaceId,
transactionManager,
);
return orphanThreads.map(({ id }) => id);
}
public async deleteByIds(
messageThreadIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`DELETE FROM ${dataSourceSchema}."messageThread" WHERE id = ANY($1)`,
[messageThreadIds],
workspaceId,
transactionManager,
);
}
public async saveMessageThreadOrReturnExistingMessageThread(
headerMessageId: string,
messageThreadExternalId: string,
dataSourceMetadata: DataSourceEntity,
workspaceId: string,
manager: EntityManager,
) {
// Check if message thread already exists via threadExternalId
const existingMessageChannelMessageAssociationByMessageThreadExternalId =
await this.messageChannelMessageAssociationService.getFirstByMessageThreadExternalId(
messageThreadExternalId,
workspaceId,
manager,
);
const existingMessageThread =
existingMessageChannelMessageAssociationByMessageThreadExternalId?.messageThreadId;
if (existingMessageThread) {
return Promise.resolve(existingMessageThread);
}
// Check if message thread already exists via existing message headerMessageId
const existingMessageWithSameHeaderMessageId =
await this.messageService.getFirstOrNullByHeaderMessageId(
headerMessageId,
workspaceId,
manager,
);
if (existingMessageWithSameHeaderMessageId) {
return Promise.resolve(
existingMessageWithSameHeaderMessageId.messageThreadId,
);
}
// If message thread does not exist, create new message thread
const newMessageThreadId = v4();
await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."messageThread" ("id") VALUES ($1)`,
[newMessageThreadId],
);
return Promise.resolve(newMessageThreadId);
}
}

View File

@ -0,0 +1,138 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
@Injectable()
export class MessageRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}
public async getNonAssociatedMessageIdsPaginated(
limit: number,
offset: number,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<string[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const nonAssociatedMessages =
await this.workspaceDataSourceService.executeRawQuery(
`SELECT m.id FROM ${dataSourceSchema}."message" m
LEFT JOIN ${dataSourceSchema}."messageChannelMessageAssociation" mcma
ON m.id = mcma."messageId"
WHERE mcma.id IS NULL
LIMIT $1 OFFSET $2`,
[limit, offset],
workspaceId,
transactionManager,
);
return nonAssociatedMessages.map(({ id }) => id);
}
public async getFirstOrNullByHeaderMessageId(
headerMessageId: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata> | null> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const messages = await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "headerMessageId" = $1 LIMIT 1`,
[headerMessageId],
workspaceId,
transactionManager,
);
if (!messages || messages.length === 0) {
return null;
}
return messages[0];
}
public async getByIds(
messageIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata>[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
return await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "id" = ANY($1)`,
[messageIds],
workspaceId,
transactionManager,
);
}
public async deleteByIds(
messageIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`DELETE FROM ${dataSourceSchema}."message" WHERE "id" = ANY($1)`,
[messageIds],
workspaceId,
transactionManager,
);
}
public async getByMessageThreadIds(
messageThreadIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata>[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
return await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "messageThreadId" = ANY($1)`,
[messageThreadIds],
workspaceId,
transactionManager,
);
}
public async insert(
id: string,
headerMessageId: string,
subject: string,
receivedAt: Date,
messageDirection: string,
messageThreadId: string,
text: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`INSERT INTO ${dataSourceSchema}."message" ("id", "headerMessageId", "subject", "receivedAt", "direction", "messageThreadId", "text") VALUES ($1, $2, $3, $4, $5, $6, $7)`,
[
id,
headerMessageId,
subject,
receivedAt,
messageDirection,
messageThreadId,
text,
],
workspaceId,
transactionManager,
);
}
}

View File

@ -1,23 +0,0 @@
import { Module, forwardRef } from '@nestjs/common';
import { MessageChannelModule } from 'src/modules/messaging/repositories/message-channel/message-channel.module';
import { MessageChannelMessageAssociationModule } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-assocation.module';
import { MessageParticipantModule } from 'src/modules/messaging/repositories/message-participant/message-participant.module';
import { MessageThreadModule } from 'src/modules/messaging/repositories/message-thread/message-thread.module';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { CreateCompaniesAndContactsModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.module';
@Module({
imports: [
WorkspaceDataSourceModule,
forwardRef(() => MessageThreadModule),
MessageParticipantModule,
MessageChannelMessageAssociationModule,
MessageChannelModule,
CreateCompaniesAndContactsModule,
],
providers: [MessageService],
exports: [MessageService],
})
export class MessageModule {}

View File

@ -0,0 +1,15 @@
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { FetchByBatchesService } from 'src/modules/messaging/services/fetch-by-batch/fetch-by-batch.service';
@Module({
imports: [
HttpModule.register({
baseURL: 'https://www.googleapis.com/batch/gmail/v1',
}),
],
providers: [FetchByBatchesService],
exports: [FetchByBatchesService],
})
export class FetchByBatchModule {}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { FetchByBatchModule } from 'src/modules/messaging/services/fetch-by-batch/fetch-by-batch.module';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches/fetch-messages-by-batches.service';
@Module({
imports: [FetchByBatchModule],
providers: [FetchMessagesByBatchesService],
exports: [FetchMessagesByBatchesService],
})
export class FetchMessagesByBatchesModule {}

View File

@ -7,7 +7,7 @@ import planer from 'planer';
import { GmailMessage } from 'src/modules/messaging/types/gmail-message';
import { MessageQuery } from 'src/modules/messaging/types/message-or-thread-query';
import { GmailMessageParsedResponse } from 'src/modules/messaging/types/gmail-message-parsed-response';
import { FetchByBatchesService } from 'src/modules/messaging/services/fetch-by-batch.service';
import { FetchByBatchesService } from 'src/modules/messaging/services/fetch-by-batch/fetch-by-batch.service';
import { formatAddressObjectAsParticipants } from 'src/modules/messaging/services/utils/format-address-object-as-participants.util';
@Injectable()

View File

@ -0,0 +1,31 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { FetchMessagesByBatchesModule } from 'src/modules/messaging/services/fetch-messages-by-batches/fetch-messages-by-batches.module';
import { GmailFullSyncService } from 'src/modules/messaging/services/gmail-full-sync/gmail-full-sync.service';
import { MessagingProvidersModule } from 'src/modules/messaging/services/providers/messaging-providers.module';
import { SaveMessagesAndCreateContactsModule } from 'src/modules/messaging/services/save-message-and-create-contact/save-message-and-create-contacts.module';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
@Module({
imports: [
MessagingProvidersModule,
FetchMessagesByBatchesModule,
ObjectMetadataRepositoryModule.forFeature([
ConnectedAccountObjectMetadata,
MessageChannelObjectMetadata,
MessageChannelMessageAssociationObjectMetadata,
BlocklistObjectMetadata,
]),
SaveMessagesAndCreateContactsModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
],
providers: [GmailFullSyncService],
exports: [GmailFullSyncService],
})
export class GmailFullSyncModule {}

View File

@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches.service';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches/fetch-messages-by-batches.service';
import { GmailClientProvider } from 'src/modules/messaging/services/providers/gmail/gmail-client.provider';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
@ -11,17 +11,22 @@ import {
GmailFullSyncJobData,
GmailFullSyncJob,
} from 'src/modules/messaging/jobs/gmail-full-sync.job';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { MessageChannelMessageAssociationService } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-association.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/repositories/message-channel-message-association.repository';
import { createQueriesFromMessageIds } from 'src/modules/messaging/utils/create-queries-from-message-ids.util';
import { gmailSearchFilterExcludeEmails } from 'src/modules/messaging/utils/gmail-search-filter.util';
import { BlocklistService } from 'src/modules/connected-account/repositories/blocklist/blocklist.service';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-messages-and-create-contacts.service';
import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-message-and-create-contact/save-messages-and-create-contacts.service';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/modules/feature-flag/feature-flag.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
@Injectable()
export class GmailFullSyncService {
@ -32,10 +37,16 @@ export class GmailFullSyncService {
private readonly fetchMessagesByBatchesService: FetchMessagesByBatchesService,
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
private readonly messageChannelService: MessageChannelService,
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationService,
private readonly blocklistService: BlocklistService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelRepository: MessageChannelRepository,
@InjectObjectMetadataRepository(
MessageChannelMessageAssociationObjectMetadata,
)
private readonly messageChannelMessageAssociationRepository: MessageChannelMessageAssociationRepository,
@InjectObjectMetadataRepository(BlocklistObjectMetadata)
private readonly blocklistRepository: BlocklistRepository,
private readonly saveMessagesAndCreateContactsService: SaveMessagesAndCreateContactsService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
@ -46,7 +57,7 @@ export class GmailFullSyncService {
connectedAccountId: string,
nextPageToken?: string,
): Promise<void> {
const connectedAccount = await this.connectedAccountService.getById(
const connectedAccount = await this.connectedAccountRepository.getById(
connectedAccountId,
workspaceId,
);
@ -70,7 +81,7 @@ export class GmailFullSyncService {
}
const gmailMessageChannel =
await this.messageChannelService.getFirstByConnectedAccountId(
await this.messageChannelRepository.getFirstByConnectedAccountId(
connectedAccountId,
workspaceId,
);
@ -99,7 +110,7 @@ export class GmailFullSyncService {
isBlocklistEnabledFeatureFlag && isBlocklistEnabledFeatureFlag.value;
const blocklist = isBlocklistEnabled
? await this.blocklistService.getByWorkspaceMemberId(
? await this.blocklistRepository.getByWorkspaceMemberId(
workspaceMemberId,
workspaceId,
)
@ -140,7 +151,7 @@ export class GmailFullSyncService {
startTime = Date.now();
const existingMessageChannelMessageAssociations =
await this.messageChannelMessageAssociationService.getByMessageExternalIdsAndMessageChannelId(
await this.messageChannelMessageAssociationRepository.getByMessageExternalIdsAndMessageChannelId(
messageExternalIds,
gmailMessageChannelId,
workspaceId,
@ -221,7 +232,7 @@ export class GmailFullSyncService {
startTime = Date.now();
await this.connectedAccountService.updateLastSyncHistoryIdIfHigher(
await this.connectedAccountRepository.updateLastSyncHistoryIdIfHigher(
historyId,
connectedAccount.id,
workspaceId,

View File

@ -0,0 +1,31 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { FetchMessagesByBatchesModule } from 'src/modules/messaging/services/fetch-messages-by-batches/fetch-messages-by-batches.module';
import { GmailPartialSyncService } from 'src/modules/messaging/services/gmail-partial-sync/gmail-partial-sync.service';
import { MessageModule } from 'src/modules/messaging/services/message/message.module';
import { MessagingProvidersModule } from 'src/modules/messaging/services/providers/messaging-providers.module';
import { SaveMessagesAndCreateContactsModule } from 'src/modules/messaging/services/save-message-and-create-contact/save-message-and-create-contacts.module';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
@Module({
imports: [
MessagingProvidersModule,
FetchMessagesByBatchesModule,
ObjectMetadataRepositoryModule.forFeature([
ConnectedAccountObjectMetadata,
MessageChannelObjectMetadata,
BlocklistObjectMetadata,
]),
MessageModule,
SaveMessagesAndCreateContactsModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
],
providers: [GmailPartialSyncService],
exports: [GmailPartialSyncService],
})
export class GmailPartialSyncModule {}

View File

@ -4,7 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { gmail_v1 } from 'googleapis';
import { Repository } from 'typeorm';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches.service';
import { FetchMessagesByBatchesService } from 'src/modules/messaging/services/fetch-messages-by-batches/fetch-messages-by-batches.service';
import { GmailClientProvider } from 'src/modules/messaging/services/providers/gmail/gmail-client.provider';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
@ -12,18 +12,22 @@ import {
GmailFullSyncJob,
GmailFullSyncJobData,
} from 'src/modules/messaging/jobs/gmail-full-sync.job';
import { ConnectedAccountService } from 'src/modules/connected-account/repositories/connected-account/connected-account.service';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { createQueriesFromMessageIds } from 'src/modules/messaging/utils/create-queries-from-message-ids.util';
import { GmailMessage } from 'src/modules/messaging/types/gmail-message';
import { isPersonEmail } from 'src/modules/messaging/utils/is-person-email.util';
import { BlocklistService } from 'src/modules/connected-account/repositories/blocklist/blocklist.service';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-messages-and-create-contacts.service';
import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-message-and-create-contact/save-messages-and-create-contacts.service';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/modules/feature-flag/feature-flag.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { BlocklistObjectMetadata } from 'src/modules/connected-account/standard-objects/blocklist.object-metadata';
import { MessageService } from 'src/modules/messaging/services/message/message.service';
@Injectable()
export class GmailPartialSyncService {
@ -34,10 +38,13 @@ export class GmailPartialSyncService {
private readonly fetchMessagesByBatchesService: FetchMessagesByBatchesService,
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
private readonly connectedAccountService: ConnectedAccountService,
private readonly messageChannelService: MessageChannelService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelRepository: MessageChannelRepository,
private readonly messageService: MessageService,
private readonly blocklistService: BlocklistService,
@InjectObjectMetadataRepository(BlocklistObjectMetadata)
private readonly blocklistRepository: BlocklistRepository,
private readonly saveMessagesAndCreateContactsService: SaveMessagesAndCreateContactsService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
@ -48,7 +55,7 @@ export class GmailPartialSyncService {
connectedAccountId: string,
maxResults = 500,
): Promise<void> {
const connectedAccount = await this.connectedAccountService.getById(
const connectedAccount = await this.connectedAccountRepository.getById(
connectedAccountId,
workspaceId,
);
@ -103,7 +110,7 @@ export class GmailPartialSyncService {
`gmail partial-sync for workspace ${workspaceId} and account ${connectedAccountId}: invalid lastSyncHistoryId, falling back to full sync.`,
);
await this.connectedAccountService.deleteHistoryId(
await this.connectedAccountRepository.deleteHistoryId(
connectedAccountId,
workspaceId,
);
@ -143,7 +150,7 @@ export class GmailPartialSyncService {
}
const gmailMessageChannel =
await this.messageChannelService.getFirstByConnectedAccountId(
await this.messageChannelRepository.getFirstByConnectedAccountId(
connectedAccountId,
workspaceId,
);
@ -183,7 +190,7 @@ export class GmailPartialSyncService {
isBlocklistEnabledFeatureFlag && isBlocklistEnabledFeatureFlag.value;
const blocklist = isBlocklistEnabled
? await this.blocklistService.getByWorkspaceMemberId(
? await this.blocklistRepository.getByWorkspaceMemberId(
connectedAccount.accountOwnerId,
workspaceId,
)
@ -251,7 +258,7 @@ export class GmailPartialSyncService {
}
startTime = Date.now();
await this.connectedAccountService.updateLastSyncHistoryId(
await this.connectedAccountRepository.updateLastSyncHistoryId(
historyId,
connectedAccount.id,
workspaceId,

View File

@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { MessageParticipantService } from 'src/modules/messaging/services/message-participant/message-participant.service';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
@Module({
imports: [
WorkspaceDataSourceModule,
ObjectMetadataRepositoryModule.forFeature([PersonObjectMetadata]),
],
providers: [MessageParticipantService],
exports: [MessageParticipantService],
})
export class MessageParticipantModule {}

View File

@ -0,0 +1,59 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ParticipantWithId } from 'src/modules/messaging/types/gmail-message';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Injectable()
export class MessageParticipantService {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@InjectObjectMetadataRepository(PersonObjectMetadata)
private readonly personRepository: PersonRepository,
) {}
public async updateMessageParticipantsAfterPeopleCreation(
participants: ParticipantWithId[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
if (!participants) return;
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const handles = participants.map((participant) => participant.handle);
const participantPersonIds = await this.personRepository.getByEmails(
handles,
workspaceId,
transactionManager,
);
const messageParticipantsToUpdate = participants.map((participant) => [
participant.id,
participantPersonIds.find(
(e: { id: string; email: string }) => e.email === participant.handle,
)?.id,
]);
if (messageParticipantsToUpdate.length === 0) return;
const valuesString = messageParticipantsToUpdate
.map((_, index) => `($${index * 2 + 1}::uuid, $${index * 2 + 2}::uuid)`)
.join(', ');
await this.workspaceDataSourceService.executeRawQuery(
`UPDATE ${dataSourceSchema}."messageParticipant" AS "messageParticipant" SET "personId" = "data"."personId"
FROM (VALUES ${valuesString}) AS "data"("id", "personId")
WHERE "messageParticipant"."id" = "data"."id"`,
messageParticipantsToUpdate.flat(),
workspaceId,
transactionManager,
);
}
}

View File

@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { MessageThreadService } from 'src/modules/messaging/services/message-thread/message-thread.service';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
@Module({
imports: [
ObjectMetadataRepositoryModule.forFeature([
MessageChannelMessageAssociationObjectMetadata,
MessageObjectMetadata,
MessageThreadObjectMetadata,
]),
],
providers: [MessageThreadService],
exports: [MessageThreadService],
})
export class MessageThreadModule {}

View File

@ -0,0 +1,73 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/repositories/message-channel-message-association.repository';
import { MessageRepository } from 'src/modules/messaging/repositories/message.repository';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageThreadRepository } from 'src/modules/messaging/repositories/message-thread.repository';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
@Injectable()
export class MessageThreadService {
constructor(
@InjectObjectMetadataRepository(
MessageChannelMessageAssociationObjectMetadata,
)
private readonly messageChannelMessageAssociationRepository: MessageChannelMessageAssociationRepository,
@InjectObjectMetadataRepository(MessageObjectMetadata)
private readonly messageRepository: MessageRepository,
@InjectObjectMetadataRepository(MessageThreadObjectMetadata)
private readonly messageThreadRepository: MessageThreadRepository,
) {}
public async saveMessageThreadOrReturnExistingMessageThread(
headerMessageId: string,
messageThreadExternalId: string,
workspaceId: string,
manager: EntityManager,
) {
// Check if message thread already exists via threadExternalId
const existingMessageChannelMessageAssociationByMessageThreadExternalId =
await this.messageChannelMessageAssociationRepository.getFirstByMessageThreadExternalId(
messageThreadExternalId,
workspaceId,
manager,
);
const existingMessageThread =
existingMessageChannelMessageAssociationByMessageThreadExternalId?.messageThreadId;
if (existingMessageThread) {
return Promise.resolve(existingMessageThread);
}
// Check if message thread already exists via existing message headerMessageId
const existingMessageWithSameHeaderMessageId =
await this.messageRepository.getFirstOrNullByHeaderMessageId(
headerMessageId,
workspaceId,
manager,
);
if (existingMessageWithSameHeaderMessageId) {
return Promise.resolve(
existingMessageWithSameHeaderMessageId.messageThreadId,
);
}
// If message thread does not exist, create new message thread
const newMessageThreadId = v4();
await this.messageThreadRepository.insert(
newMessageThreadId,
workspaceId,
manager,
);
return Promise.resolve(newMessageThreadId);
}
}

View File

@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { MessageThreadModule } from 'src/modules/messaging/services/message-thread/message-thread.module';
import { MessageService } from 'src/modules/messaging/services/message/message.service';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
@Module({
imports: [
WorkspaceDataSourceModule,
ObjectMetadataRepositoryModule.forFeature([
MessageChannelMessageAssociationObjectMetadata,
MessageObjectMetadata,
MessageChannelObjectMetadata,
MessageThreadObjectMetadata,
]),
MessageThreadModule,
],
providers: [MessageService],
exports: [MessageService],
})
export class MessageModule {}

View File

@ -1,17 +1,23 @@
import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { DataSource, EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
import { DataSourceEntity } from 'src/engine-metadata/data-source/data-source.entity';
import { GmailMessage } from 'src/modules/messaging/types/gmail-message';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { MessageChannelMessageAssociationService } from 'src/modules/messaging/repositories/message-channel-message-association/message-channel-message-association.service';
import { MessageThreadService } from 'src/modules/messaging/repositories/message-thread/message-thread.service';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/repositories/message-channel-message-association.repository';
import { MessageRepository } from 'src/modules/messaging/repositories/message.repository';
import { MessageChannelMessageAssociationObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel-message-association.object-metadata';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
import { GmailMessage } from 'src/modules/messaging/types/gmail-message';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageThreadService } from 'src/modules/messaging/services/message-thread/message-thread.service';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageThreadRepository } from 'src/modules/messaging/repositories/message-thread.repository';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Injectable()
export class MessageService {
@ -19,106 +25,19 @@ export class MessageService {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly messageChannelMessageAssociationService: MessageChannelMessageAssociationService,
@Inject(forwardRef(() => MessageThreadService))
@InjectObjectMetadataRepository(
MessageChannelMessageAssociationObjectMetadata,
)
private readonly messageChannelMessageAssociationRepository: MessageChannelMessageAssociationRepository,
@InjectObjectMetadataRepository(MessageObjectMetadata)
private readonly messageRepository: MessageRepository,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelRepository: MessageChannelRepository,
@InjectObjectMetadataRepository(MessageThreadObjectMetadata)
private readonly messageThreadRepository: MessageThreadRepository,
private readonly messageThreadService: MessageThreadService,
private readonly messageChannelService: MessageChannelService,
) {}
public async getNonAssociatedMessageIdsPaginated(
limit: number,
offset: number,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<string[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const nonAssociatedMessages =
await this.workspaceDataSourceService.executeRawQuery(
`SELECT m.id FROM ${dataSourceSchema}."message" m
LEFT JOIN ${dataSourceSchema}."messageChannelMessageAssociation" mcma
ON m.id = mcma."messageId"
WHERE mcma.id IS NULL
LIMIT $1 OFFSET $2`,
[limit, offset],
workspaceId,
transactionManager,
);
return nonAssociatedMessages.map(({ id }) => id);
}
public async getFirstOrNullByHeaderMessageId(
headerMessageId: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata> | null> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const messages = await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "headerMessageId" = $1 LIMIT 1`,
[headerMessageId],
workspaceId,
transactionManager,
);
if (!messages || messages.length === 0) {
return null;
}
return messages[0];
}
public async getByIds(
messageIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata>[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
return await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "id" = ANY($1)`,
[messageIds],
workspaceId,
transactionManager,
);
}
public async deleteByIds(
messageIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
await this.workspaceDataSourceService.executeRawQuery(
`DELETE FROM ${dataSourceSchema}."message" WHERE "id" = ANY($1)`,
[messageIds],
workspaceId,
transactionManager,
);
}
public async getByMessageThreadIds(
messageThreadIds: string[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<MessageObjectMetadata>[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
return await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."message" WHERE "messageThreadId" = ANY($1)`,
[messageThreadIds],
workspaceId,
transactionManager,
);
}
public async saveMessages(
messages: GmailMessage[],
dataSourceMetadata: DataSourceEntity,
@ -140,7 +59,7 @@ export class MessageService {
await workspaceDataSource?.transaction(
async (manager: EntityManager) => {
const gmailMessageChannel =
await this.messageChannelService.getByIds(
await this.messageChannelRepository.getByIds(
[gmailMessageChannelId],
workspaceId,
manager,
@ -157,7 +76,7 @@ export class MessageService {
}
const existingMessageChannelMessageAssociationsCount =
await this.messageChannelMessageAssociationService.countByMessageExternalIdsAndMessageChannelId(
await this.messageChannelMessageAssociationRepository.countByMessageExternalIdsAndMessageChannelId(
[message.externalId],
gmailMessageChannelId,
workspaceId,
@ -173,7 +92,6 @@ export class MessageService {
await this.messageThreadService.saveMessageThreadOrReturnExistingMessageThread(
message.headerMessageId,
message.messageThreadExternalId,
dataSourceMetadata,
workspaceId,
manager,
);
@ -193,15 +111,14 @@ export class MessageService {
savedOrExistingMessageId,
);
await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."messageChannelMessageAssociation" ("messageChannelId", "messageId", "messageExternalId", "messageThreadId", "messageThreadExternalId") VALUES ($1, $2, $3, $4, $5)`,
[
await this.messageChannelMessageAssociationRepository.insert(
gmailMessageChannelId,
savedOrExistingMessageId,
message.externalId,
savedOrExistingMessageThreadId,
message.messageThreadExternalId,
],
workspaceId,
manager,
);
},
);
@ -223,7 +140,8 @@ export class MessageService {
workspaceId: string,
manager: EntityManager,
): Promise<string> {
const existingMessage = await this.getFirstOrNullByHeaderMessageId(
const existingMessage =
await this.messageRepository.getFirstOrNullByHeaderMessageId(
message.headerMessageId,
workspaceId,
);
@ -240,9 +158,7 @@ export class MessageService {
const receivedAt = new Date(parseInt(message.internalDate));
await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."message" ("id", "headerMessageId", "subject", "receivedAt", "direction", "messageThreadId", "text") VALUES ($1, $2, $3, $4, $5, $6, $7)`,
[
await this.messageRepository.insert(
newMessageId,
message.headerMessageId,
message.subject,
@ -250,7 +166,8 @@ export class MessageService {
messageDirection,
messageThreadId,
message.text,
],
workspaceId,
manager,
);
return Promise.resolve(newMessageId);
@ -268,7 +185,7 @@ export class MessageService {
await workspaceDataSource?.transaction(async (manager: EntityManager) => {
const messageChannelMessageAssociationsToDelete =
await this.messageChannelMessageAssociationService.getByMessageExternalIdsAndMessageChannelId(
await this.messageChannelMessageAssociationRepository.getByMessageExternalIdsAndMessageChannelId(
messagesDeletedMessageExternalIds,
gmailMessageChannelId,
workspaceId,
@ -281,7 +198,7 @@ export class MessageService {
messageChannelMessageAssociationToDelete.id,
);
await this.messageChannelMessageAssociationService.deleteByIds(
await this.messageChannelMessageAssociationRepository.deleteByIds(
messageChannelMessageAssociationIdsToDeleteIds,
workspaceId,
manager,
@ -294,7 +211,7 @@ export class MessageService {
);
const messageChannelMessageAssociationByMessageIds =
await this.messageChannelMessageAssociationService.getByMessageIds(
await this.messageChannelMessageAssociationRepository.getByMessageIds(
messageIdsFromMessageChannelMessageAssociationsToDelete,
workspaceId,
manager,
@ -314,7 +231,11 @@ export class MessageService {
),
);
await this.deleteByIds(messageIdsToDelete, workspaceId, manager);
await this.messageRepository.deleteByIds(
messageIdsToDelete,
workspaceId,
manager,
);
const messageThreadIdsFromMessageChannelMessageAssociationsToDelete =
messageChannelMessageAssociationsToDelete.map(
@ -322,7 +243,8 @@ export class MessageService {
messageChannelMessageAssociationToDelete.messageThreadId,
);
const messagesByThreadIds = await this.getByMessageThreadIds(
const messagesByThreadIds =
await this.messageRepository.getByMessageThreadIds(
messageThreadIdsFromMessageChannelMessageAssociationsToDelete,
workspaceId,
manager,
@ -336,7 +258,7 @@ export class MessageService {
),
);
await this.messageThreadService.deleteByIds(
await this.messageThreadRepository.deleteByIds(
threadIdsToDelete,
workspaceId,
manager,

View File

@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { CreateCompaniesAndContactsModule } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.module';
import { MessageParticipantModule } from 'src/modules/messaging/services/message-participant/message-participant.module';
import { MessageModule } from 'src/modules/messaging/services/message/message.module';
import { SaveMessagesAndCreateContactsService } from 'src/modules/messaging/services/save-message-and-create-contact/save-messages-and-create-contacts.service';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
@Module({
imports: [
MessageModule,
ObjectMetadataRepositoryModule.forFeature([
MessageChannelObjectMetadata,
MessageParticipantObjectMetadata,
]),
CreateCompaniesAndContactsModule,
MessageParticipantModule,
WorkspaceDataSourceModule,
],
providers: [SaveMessagesAndCreateContactsService],
exports: [SaveMessagesAndCreateContactsService],
})
export class SaveMessagesAndCreateContactsModule {}

View File

@ -2,9 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { MessageChannelService } from 'src/modules/messaging/repositories/message-channel/message-channel.service';
import { MessageParticipantService } from 'src/modules/messaging/repositories/message-participant/message-participant.service';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageParticipantRepository } from 'src/modules/messaging/repositories/message-participant.repository';
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company-and-contact/create-company-and-contact.service';
import {
GmailMessage,
@ -13,6 +12,11 @@ import {
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
import { MessageService } from 'src/modules/messaging/services/message/message.service';
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
import { MessageParticipantService } from 'src/modules/messaging/services/message-participant/message-participant.service';
@Injectable()
export class SaveMessagesAndCreateContactsService {
@ -22,7 +26,10 @@ export class SaveMessagesAndCreateContactsService {
constructor(
private readonly messageService: MessageService,
private readonly messageChannelService: MessageChannelService,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelRepository: MessageChannelRepository,
@InjectObjectMetadataRepository(MessageParticipantObjectMetadata)
private readonly messageParticipantRepository: MessageParticipantRepository,
private readonly createCompaniesAndContactsService: CreateCompanyAndContactService,
private readonly messageParticipantService: MessageParticipantService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@ -60,7 +67,7 @@ export class SaveMessagesAndCreateContactsService {
);
const gmailMessageChannel =
await this.messageChannelService.getFirstByConnectedAccountId(
await this.messageChannelRepository.getFirstByConnectedAccountId(
connectedAccount.id,
workspaceId,
);
@ -111,7 +118,7 @@ export class SaveMessagesAndCreateContactsService {
);
const messageParticipantsWithoutPersonIdAndWorkspaceMemberId =
await this.messageParticipantService.getByHandlesWithoutPersonIdAndWorkspaceMemberId(
await this.messageParticipantRepository.getByHandlesWithoutPersonIdAndWorkspaceMemberId(
handles,
workspaceId,
);
@ -157,7 +164,7 @@ export class SaveMessagesAndCreateContactsService {
jobName?: string,
) {
try {
await this.messageParticipantService.saveMessageParticipants(
await this.messageParticipantRepository.saveMessageParticipants(
participantsWithMessageId,
workspaceId,
);

View File

@ -1,17 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.module';
import { MessageThreadModule } from 'src/modules/messaging/repositories/message-thread/message-thread.module';
import { MessageModule } from 'src/modules/messaging/repositories/message/message.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { ThreadCleanerService } from 'src/modules/messaging/services/thread-cleaner/thread-cleaner.service';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
@Module({
imports: [
DataSourceModule,
TypeORMModule,
MessageThreadModule,
MessageModule,
ObjectMetadataRepositoryModule.forFeature([
MessageObjectMetadata,
MessageThreadObjectMetadata,
]),
],
providers: [ThreadCleanerService],
exports: [ThreadCleanerService],

View File

@ -1,37 +1,40 @@
import { Injectable } from '@nestjs/common';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { DataSourceService } from 'src/engine-metadata/data-source/data-source.service';
import { MessageThreadService } from 'src/modules/messaging/repositories/message-thread/message-thread.service';
import { MessageService } from 'src/modules/messaging/repositories/message/message.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { MessageThreadRepository } from 'src/modules/messaging/repositories/message-thread.repository';
import { MessageRepository } from 'src/modules/messaging/repositories/message.repository';
import { deleteUsingPagination } from 'src/modules/messaging/services/thread-cleaner/utils/delete-using-pagination.util';
import { MessageThreadObjectMetadata } from 'src/modules/messaging/standard-objects/message-thread.object-metadata';
import { MessageObjectMetadata } from 'src/modules/messaging/standard-objects/message.object-metadata';
@Injectable()
export class ThreadCleanerService {
constructor(
private readonly dataSourceService: DataSourceService,
private readonly typeORMService: TypeORMService,
private readonly messageService: MessageService,
private readonly messageThreadService: MessageThreadService,
@InjectObjectMetadataRepository(MessageObjectMetadata)
private readonly messageRepository: MessageRepository,
@InjectObjectMetadataRepository(MessageThreadObjectMetadata)
private readonly messageThreadRepository: MessageThreadRepository,
) {}
public async cleanWorkspaceThreads(workspaceId: string) {
await deleteUsingPagination(
workspaceId,
500,
this.messageService.getNonAssociatedMessageIdsPaginated.bind(
this.messageService,
this.messageRepository.getNonAssociatedMessageIdsPaginated.bind(
this.messageRepository,
),
this.messageService.deleteByIds.bind(this.messageService),
this.messageRepository.deleteByIds.bind(this.messageRepository),
);
await deleteUsingPagination(
workspaceId,
500,
this.messageThreadService.getOrphanThreadIdsPaginated.bind(
this.messageThreadService,
this.messageThreadRepository.getOrphanThreadIdsPaginated.bind(
this.messageThreadRepository,
),
this.messageThreadRepository.deleteByIds.bind(
this.messageThreadRepository,
),
this.messageThreadService.deleteByIds.bind(this.messageThreadService),
);
}
}

View File

@ -6,9 +6,8 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
// TODO: Move outside of the messaging module
@Injectable()
export class PersonService {
export class PersonRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { PersonService } from 'src/modules/person/repositories/person/person.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [PersonService],
exports: [PersonService],
})
export class PersonModule {}

View File

@ -6,9 +6,8 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
// TODO: Move outside of the messaging module
@Injectable()
export class WorkspaceMemberService {
export class WorkspaceMemberRepository {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}

View File

@ -1,11 +0,0 @@
import { Module } from '@nestjs/common';
import { WorkspaceMemberService } from 'src/modules/workspace-member/repositories/workspace-member/workspace-member.service';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [WorkspaceDataSourceModule],
providers: [WorkspaceMemberService],
exports: [WorkspaceMemberService],
})
export class WorkspaceMemberModule {}