4971 add issyncenabled toggle in messaging settings (#4995)

- Closes #4971
- Fix calendar import to take isSyncEnabled into account
This commit is contained in:
bosiraphael
2024-04-17 13:35:23 +02:00
committed by GitHub
parent 67db7d85c0
commit 3024e04a1c
16 changed files with 147 additions and 97 deletions

View File

@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { GoogleCalendarSyncCommand } from 'src/modules/calendar/commands/google-calendar-sync.command';
import { WorkspaceGoogleCalendarSyncModule } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
@ -11,6 +12,7 @@ import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/st
ConnectedAccountObjectMetadata,
CalendarChannelObjectMetadata,
]),
WorkspaceGoogleCalendarSyncModule,
],
providers: [GoogleCalendarSyncCommand],
})

View File

@ -1,18 +1,6 @@
import { Inject } from '@nestjs/common';
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 { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import {
GoogleCalendarSyncJobData,
GoogleCalendarSyncJob,
} from 'src/modules/calendar/jobs/google-calendar-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';
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
import { WorkspaceGoogleCalendarSyncService } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service';
interface GoogleCalendarSyncOptions {
workspaceId: string;
@ -25,25 +13,11 @@ interface GoogleCalendarSyncOptions {
})
export class GoogleCalendarSyncCommand extends CommandRunner {
constructor(
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(CalendarChannelObjectMetadata)
private readonly calendarChannelRepository: CalendarChannelRepository,
private readonly workspaceGoogleCalendarSyncService: WorkspaceGoogleCalendarSyncService,
) {
super();
}
async run(
_passedParam: string[],
options: GoogleCalendarSyncOptions,
): Promise<void> {
await this.fetchWorkspaceCalendars(options.workspaceId);
return;
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description: 'workspace id',
@ -53,31 +27,14 @@ export class GoogleCalendarSyncCommand extends CommandRunner {
return value;
}
private async fetchWorkspaceCalendars(workspaceId: string): Promise<void> {
const connectedAccounts =
await this.connectedAccountRepository.getAll(workspaceId);
async run(
_passedParam: string[],
options: GoogleCalendarSyncOptions,
): Promise<void> {
await this.workspaceGoogleCalendarSyncService.startWorkspaceGoogleCalendarSync(
options.workspaceId,
);
for (const connectedAccount of connectedAccounts) {
const calendarChannel =
await this.calendarChannelRepository.getFirstByConnectedAccountId(
connectedAccount.id,
workspaceId,
);
if (!calendarChannel?.isSyncEnabled) {
continue;
}
await this.messageQueueService.add<GoogleCalendarSyncJobData>(
GoogleCalendarSyncJob.name,
{
workspaceId,
connectedAccountId: connectedAccount.id,
},
{
retryLimit: 2,
},
);
}
return;
}
}

View File

@ -11,10 +11,7 @@ import {
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync.service';
import { WorkspaceGoogleCalendarSyncService } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service';
@Injectable()
export class GoogleCalendarSyncCronJob implements MessageQueueJob<undefined> {
@ -23,11 +20,9 @@ export class GoogleCalendarSyncCronJob implements MessageQueueJob<undefined> {
private readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(DataSourceEntity, 'metadata')
private readonly dataSourceRepository: Repository<DataSourceEntity>,
@InjectObjectMetadataRepository(CalendarChannelObjectMetadata)
private readonly calendarChannelRepository: CalendarChannelRepository,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
private readonly googleCalendarSyncService: GoogleCalendarSyncService,
private readonly workspaceGoogleCalendarSyncService: WorkspaceGoogleCalendarSyncService,
) {}
async handle(): Promise<void> {
@ -62,20 +57,8 @@ export class GoogleCalendarSyncCronJob implements MessageQueueJob<undefined> {
);
for (const workspaceId of workspaceIdsWithDataSources) {
await this.startWorkspaceGoogleCalendarSync(workspaceId);
}
}
private async startWorkspaceGoogleCalendarSync(
workspaceId: string,
): Promise<void> {
const calendarChannels =
await this.calendarChannelRepository.getAll(workspaceId);
for (const calendarChannel of calendarChannels) {
await this.googleCalendarSyncService.startGoogleCalendarSync(
await this.workspaceGoogleCalendarSyncService.startWorkspaceGoogleCalendarSync(
workspaceId,
calendarChannel.connectedAccountId,
);
}
}

View File

@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync.service';
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service';
export type GoogleCalendarSyncJobData = {
workspaceId: string;

View File

@ -6,7 +6,7 @@ import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repos
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { CalendarEventCleanerModule } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module';
import { CalendarEventParticipantModule } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module';
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync.service';
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync/google-calendar-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';

View File

@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceGoogleCalendarSyncService } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
@Module({
imports: [
ObjectMetadataRepositoryModule.forFeature([CalendarChannelObjectMetadata]),
],
providers: [WorkspaceGoogleCalendarSyncService],
exports: [WorkspaceGoogleCalendarSyncService],
})
export class WorkspaceGoogleCalendarSyncModule {}

View File

@ -0,0 +1,46 @@
import { Injectable } from '@nestjs/common';
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import {
GoogleCalendarSyncJobData,
GoogleCalendarSyncJob,
} from 'src/modules/calendar/jobs/google-calendar-sync.job';
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel.object-metadata';
@Injectable()
export class WorkspaceGoogleCalendarSyncService {
constructor(
@InjectObjectMetadataRepository(CalendarChannelObjectMetadata)
private readonly calendarChannelRepository: CalendarChannelRepository,
@InjectMessageQueue(MessageQueue.calendarQueue)
private readonly messageQueueService: MessageQueueService,
) {}
public async startWorkspaceGoogleCalendarSync(
workspaceId: string,
): Promise<void> {
const calendarChannels =
await this.calendarChannelRepository.getAll(workspaceId);
for (const calendarChannel of calendarChannels) {
if (!calendarChannel?.isSyncEnabled) {
continue;
}
await this.messageQueueService.add<GoogleCalendarSyncJobData>(
GoogleCalendarSyncJob.name,
{
workspaceId,
connectedAccountId: calendarChannel.connectedAccountId,
},
{
retryLimit: 2,
},
);
}
}
}

View File

@ -5,17 +5,17 @@ import { Repository, In } from 'typeorm';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.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 { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/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';
import {
GmailPartialSyncJob as GmailPartialSyncJob,
GmailPartialSyncJobData as GmailPartialSyncJobData,
GmailPartialSyncJobData,
GmailPartialSyncJob,
} from 'src/modules/messaging/jobs/gmail-partial-sync.job';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
@Injectable()
export class GmailPartialSyncCronJob implements MessageQueueJob<undefined> {
@ -28,8 +28,8 @@ export class GmailPartialSyncCronJob implements MessageQueueJob<undefined> {
private readonly dataSourceRepository: Repository<DataSourceEntity>,
@Inject(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(MessageChannelObjectMetadata)
private readonly messageChannelRepository: MessageChannelRepository,
) {}
async handle(): Promise<void> {
@ -59,15 +59,22 @@ export class GmailPartialSyncCronJob implements MessageQueueJob<undefined> {
private async enqueuePartialSyncs(workspaceId: string): Promise<void> {
try {
const connectedAccounts =
await this.connectedAccountRepository.getAll(workspaceId);
const messageChannels =
await this.messageChannelRepository.getAll(workspaceId);
for (const messageChannel of messageChannels) {
if (!messageChannel?.isSyncEnabled) {
continue;
}
for (const connectedAccount of connectedAccounts) {
await this.messageQueueService.add<GmailPartialSyncJobData>(
GmailPartialSyncJob.name,
{
workspaceId,
connectedAccountId: connectedAccount.id,
connectedAccountId: messageChannel.connectedAccountId,
},
{
retryLimit: 2,
},
);
}

View File

@ -63,7 +63,9 @@ export class GmailFetchMessageContentFromCacheService {
return;
}
if (connectedAccount.authFailedAt) {
const { accessToken, refreshToken, authFailedAt } = connectedAccount;
if (authFailedAt) {
this.logger.error(
`Connected account ${connectedAccountId} in workspace ${workspaceId} is in a failed state. Skipping...`,
);
@ -71,9 +73,6 @@ export class GmailFetchMessageContentFromCacheService {
return;
}
const accessToken = connectedAccount.accessToken;
const refreshToken = connectedAccount.refreshToken;
if (!refreshToken) {
throw new Error(
`No refresh token found for connected account ${connectedAccountId} in workspace ${workspaceId}`,

View File

@ -127,6 +127,16 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata {
})
isContactAutoCreationEnabled: boolean;
@FieldMetadata({
standardId: messageChannelStandardFieldIds.isSyncEnabled,
type: FieldMetadataType.BOOLEAN,
label: 'Is Sync Enabled',
description: 'Is Sync Enabled',
icon: 'IconRefresh',
defaultValue: true,
})
isSyncEnabled: boolean;
@FieldMetadata({
standardId:
messageChannelStandardFieldIds.messageChannelMessageAssociations,