[Microsoft integration] getFullMessageList (#9544)

Creation of the GmailGetMessageListService
Implementation of the driver to MS Graph API getFullMessageList
This commit is contained in:
Guillim
2025-01-13 10:13:37 +01:00
committed by GitHub
parent c1847054f8
commit 34ee64a36c
7 changed files with 184 additions and 7 deletions

View File

@ -4,7 +4,8 @@ import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decora
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { CalendarEventListFetchCronJob } from 'src/modules/calendar/calendar-event-import-manager/crons/jobs/calendar-event-list-fetch.cron.job';
import { CALENDAR_EVENTS_IMPORT_CRON_PATTERN } from 'src/modules/calendar/calendar-event-import-manager/crons/jobs/calendar-events-import.cron.job';
const CALENDAR_EVENTS_LIST_CRON_PATTERN = '*/5 * * * *';
@Command({
name: 'cron:calendar:calendar-event-list-fetch',
@ -23,7 +24,9 @@ export class CalendarEventListFetchCronCommand extends CommandRunner {
CalendarEventListFetchCronJob.name,
undefined,
{
repeat: { pattern: CALENDAR_EVENTS_IMPORT_CRON_PATTERN },
repeat: {
pattern: CALENDAR_EVENTS_LIST_CRON_PATTERN,
},
},
);
}

View File

@ -0,0 +1,30 @@
import { Module } from '@nestjs/common';
import { EnvironmentModule } from 'src/engine/core-modules/environment/environment.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { MicrosoftOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module';
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { MicrosoftGetMessageListService } from './services/microsoft-get-message-list.service';
@Module({
imports: [
EnvironmentModule,
MessagingCommonModule,
FeatureFlagModule,
OAuth2ClientManagerModule,
WorkspaceDataSourceModule,
ObjectMetadataRepositoryModule,
],
providers: [
MicrosoftClientProvider,
MicrosoftGetMessageListService,
MicrosoftOAuth2ClientManagerService,
],
exports: [MicrosoftGetMessageListService, MicrosoftClientProvider],
})
export class MessagingMicrosoftDriverModule {}

View File

@ -0,0 +1,32 @@
import { Injectable } from '@nestjs/common';
import { Client } from '@microsoft/microsoft-graph-client';
import { MicrosoftOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@Injectable()
export class MicrosoftClientProvider {
constructor(
private readonly microsoftOAuth2ClientManagerService: MicrosoftOAuth2ClientManagerService,
) {}
public async getMicrosoftClient(
connectedAccount: Pick<
ConnectedAccountWorkspaceEntity,
'refreshToken' | 'id'
>,
): Promise<Client> {
try {
return await this.microsoftOAuth2ClientManagerService.getOAuth2Client(
connectedAccount.refreshToken,
);
} catch (error) {
throw new Error(
`Failed to get Microsoft client: ${
error instanceof Error ? error.message : 'Unknown error'
}`,
);
}
}
}

View File

@ -0,0 +1,57 @@
import { Injectable } from '@nestjs/common';
import {
PageCollection,
PageIterator,
PageIteratorCallback,
} from '@microsoft/microsoft-graph-client';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { GetFullMessageListResponse } from 'src/modules/messaging/message-import-manager/services/messaging-get-message-list.service';
// Microsoft API limit is 1000 messages per request on this endpoint
const MESSAGING_MICROSOFT_USERS_MESSAGES_LIST_MAX_RESULT = 1000;
@Injectable()
export class MicrosoftGetMessageListService {
constructor(
private readonly microsoftClientProvider: MicrosoftClientProvider,
) {}
public async getFullMessageList(
connectedAccount: Pick<
ConnectedAccountWorkspaceEntity,
'provider' | 'refreshToken' | 'id'
>,
syncCursor?: string,
): Promise<GetFullMessageListResponse> {
const messageExternalIds: string[] = [];
const microsoftClient =
await this.microsoftClientProvider.getMicrosoftClient(connectedAccount);
const response: PageCollection = await microsoftClient
.api(syncCursor || '/me/mailfolders/inbox/messages/delta?$select=id')
.version('beta')
.headers({
Prefer: `odata.maxpagesize=${MESSAGING_MICROSOFT_USERS_MESSAGES_LIST_MAX_RESULT}`,
})
.get();
const callback: PageIteratorCallback = (data) => {
messageExternalIds.push(data.id);
return true;
};
const pageIterator = new PageIterator(microsoftClient, response, callback);
await pageIterator.iterate();
return {
messageExternalIds: messageExternalIds,
nextSyncCursor: pageIterator.getDeltaLink() || '',
};
}
}

View File

@ -18,6 +18,7 @@ import { MessagingMessageListFetchCronJob } from 'src/modules/messaging/message-
import { MessagingMessagesImportCronJob } from 'src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job';
import { MessagingOngoingStaleCronJob } from 'src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job';
import { MessagingGmailDriverModule } from 'src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module';
import { MessagingMicrosoftDriverModule } from 'src/modules/messaging/message-import-manager/drivers/microsoft/messaging-microsoft-driver.module';
import { MessagingAddSingleMessageToCacheForImportJob } from 'src/modules/messaging/message-import-manager/jobs/messaging-add-single-message-to-cache-for-import.job';
import { MessagingCleanCacheJob } from 'src/modules/messaging/message-import-manager/jobs/messaging-clean-cache';
import { MessagingMessageListFetchJob } from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
@ -40,6 +41,7 @@ import { MessagingMonitoringModule } from 'src/modules/messaging/monitoring/mess
RefreshAccessTokenManagerModule,
WorkspaceDataSourceModule,
MessagingGmailDriverModule,
MessagingMicrosoftDriverModule,
MessagingCommonModule,
TypeOrmModule.forFeature([Workspace], 'core'),
TypeOrmModule.forFeature([DataSourceEntity], 'metadata'),

View File

@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { GmailGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-get-message-list.service';
import { MicrosoftGetMessageListService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-message-list.service';
import {
MessageImportException,
MessageImportExceptionCode,
@ -22,6 +23,7 @@ export type GetPartialMessageListResponse = {
export class MessagingGetMessageListService {
constructor(
private readonly gmailGetMessageListService: GmailGetMessageListService,
private readonly microsoftGetMessageListService: MicrosoftGetMessageListService,
) {}
public async getFullMessageList(
@ -36,11 +38,9 @@ export class MessagingGetMessageListService {
connectedAccount,
);
case 'microsoft':
// TODO: Placeholder
return {
messageExternalIds: [],
nextSyncCursor: '',
};
return this.microsoftGetMessageListService.getFullMessageList(
connectedAccount,
);
default:
throw new MessageImportException(
`Provider ${connectedAccount.provider} is not supported`,