[Microsoft integration] getFullMessageList (#9544)
Creation of the GmailGetMessageListService Implementation of the driver to MS Graph API getFullMessageList
This commit is contained in:
@ -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 { 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 { 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 { 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({
|
@Command({
|
||||||
name: 'cron:calendar:calendar-event-list-fetch',
|
name: 'cron:calendar:calendar-event-list-fetch',
|
||||||
@ -23,7 +24,9 @@ export class CalendarEventListFetchCronCommand extends CommandRunner {
|
|||||||
CalendarEventListFetchCronJob.name,
|
CalendarEventListFetchCronJob.name,
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
repeat: { pattern: CALENDAR_EVENTS_IMPORT_CRON_PATTERN },
|
repeat: {
|
||||||
|
pattern: CALENDAR_EVENTS_LIST_CRON_PATTERN,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {}
|
||||||
@ -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'
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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() || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 { 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 { 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 { 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 { 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 { 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';
|
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,
|
RefreshAccessTokenManagerModule,
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
MessagingGmailDriverModule,
|
MessagingGmailDriverModule,
|
||||||
|
MessagingMicrosoftDriverModule,
|
||||||
MessagingCommonModule,
|
MessagingCommonModule,
|
||||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||||
TypeOrmModule.forFeature([DataSourceEntity], 'metadata'),
|
TypeOrmModule.forFeature([DataSourceEntity], 'metadata'),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
|
|
||||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
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 { 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 {
|
import {
|
||||||
MessageImportException,
|
MessageImportException,
|
||||||
MessageImportExceptionCode,
|
MessageImportExceptionCode,
|
||||||
@ -22,6 +23,7 @@ export type GetPartialMessageListResponse = {
|
|||||||
export class MessagingGetMessageListService {
|
export class MessagingGetMessageListService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly gmailGetMessageListService: GmailGetMessageListService,
|
private readonly gmailGetMessageListService: GmailGetMessageListService,
|
||||||
|
private readonly microsoftGetMessageListService: MicrosoftGetMessageListService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async getFullMessageList(
|
public async getFullMessageList(
|
||||||
@ -36,11 +38,9 @@ export class MessagingGetMessageListService {
|
|||||||
connectedAccount,
|
connectedAccount,
|
||||||
);
|
);
|
||||||
case 'microsoft':
|
case 'microsoft':
|
||||||
// TODO: Placeholder
|
return this.microsoftGetMessageListService.getFullMessageList(
|
||||||
return {
|
connectedAccount,
|
||||||
messageExternalIds: [],
|
);
|
||||||
nextSyncCursor: '',
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
throw new MessageImportException(
|
throw new MessageImportException(
|
||||||
`Provider ${connectedAccount.provider} is not supported`,
|
`Provider ${connectedAccount.provider} is not supported`,
|
||||||
|
|||||||
@ -54,6 +54,59 @@ Register the following recurring jobs:
|
|||||||
yarn command:prod cron:messaging:messages-import
|
yarn command:prod cron:messaging:messages-import
|
||||||
yarn command:prod cron:messaging:message-list-fetch
|
yarn command:prod cron:messaging:message-list-fetch
|
||||||
yarn command:prod cron:calendar:calendar-event-list-fetch
|
yarn command:prod cron:calendar:calendar-event-list-fetch
|
||||||
|
yarn command:prod cron:calendar:calendar-event-import
|
||||||
|
yarn command:prod cron:messaging:ongoing-stale
|
||||||
|
yarn command:prod cron:calendar:ongoing-stale
|
||||||
|
```
|
||||||
|
|
||||||
|
## For Outlook and Outlook Calendar (Microsoft 365)
|
||||||
|
|
||||||
|
### Create a project in Microsoft Azure
|
||||||
|
|
||||||
|
You will need to create a project in [Microsoft Azure](https://portal.azure.com/#view/Microsoft_AAD_IAM/AppGalleryBladeV2) and get the credentials.
|
||||||
|
|
||||||
|
Then you can set the following environment variables:
|
||||||
|
|
||||||
|
- `AUTH_MICROSOFT_ENABLED=true`
|
||||||
|
- `AUTH_MICROSOFT_CLIENT_ID=<client-id>`
|
||||||
|
- `AUTH_MICROSOFT_TENANT_ID=<tenant-id>`
|
||||||
|
- `AUTH_MICROSOFT_CLIENT_SECRET=<client-secret>`
|
||||||
|
- `AUTH_MICROSOFT_CALLBACK_URL=https://<your-domain>/auth/microsoft/redirect` if you want to use Microsoft SSO
|
||||||
|
- `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://<your-domain>/auth/microsoft-apis/get-access-token`
|
||||||
|
|
||||||
|
### Enable APIs
|
||||||
|
|
||||||
|
On Microsoft Azure Console enable the following APIs in "Permissions":
|
||||||
|
|
||||||
|
- Microsoft Graph: Mail.Read
|
||||||
|
- Microsoft Graph: Calendars.Read
|
||||||
|
- Microsoft Graph: User.Read.All
|
||||||
|
- Microsoft Graph: openid
|
||||||
|
- Microsoft Graph: email
|
||||||
|
- Microsoft Graph: profile
|
||||||
|
- Microsoft Graph: offline_access
|
||||||
|
|
||||||
|
### Authorized redirect URIs
|
||||||
|
|
||||||
|
You need to add the following redirect URIs to your project:
|
||||||
|
- `https://<your-domain>/auth/microsoft/redirect` if you want to use Microsoft SSO
|
||||||
|
- `https://<your-domain>/auth/microsoft-apis/get-access-token`
|
||||||
|
|
||||||
|
### If your app is in test mode
|
||||||
|
|
||||||
|
If your app is in test mode, you will need to add test users to your project.
|
||||||
|
|
||||||
|
Add your test users to the "Users and groups" section.
|
||||||
|
|
||||||
|
### Start the cron jobs
|
||||||
|
|
||||||
|
Register the following recurring jobs:
|
||||||
|
```
|
||||||
|
# from your worker container
|
||||||
|
yarn command:prod cron:messaging:messages-import
|
||||||
|
yarn command:prod cron:messaging:message-list-fetch
|
||||||
|
yarn command:prod cron:calendar:calendar-event-list-fetch
|
||||||
|
yarn command:prod cron:calendar:calendar-event-import
|
||||||
yarn command:prod cron:messaging:ongoing-stale
|
yarn command:prod cron:messaging:ongoing-stale
|
||||||
yarn command:prod cron:calendar:ongoing-stale
|
yarn command:prod cron:calendar:ongoing-stale
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user