Reuse OAuth access tokens (#15089)
This commit is contained in:
+2
@@ -23,6 +23,7 @@ import { MicrosoftCalendarDriverModule } from 'src/modules/calendar/calendar-eve
|
||||
import { CalendarEventListFetchJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
|
||||
import { CalendarEventsImportJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job';
|
||||
import { CalendarOngoingStaleJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job';
|
||||
import { CalendarAccountAuthenticationService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-account-authentication.service';
|
||||
import { CalendarEventImportErrorHandlerService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service';
|
||||
import { CalendarEventsImportService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service';
|
||||
import { CalendarFetchEventsService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service';
|
||||
@@ -51,6 +52,7 @@ import { RefreshTokensManagerModule } from 'src/modules/connected-account/refres
|
||||
MetricsModule,
|
||||
],
|
||||
providers: [
|
||||
CalendarAccountAuthenticationService,
|
||||
CalendarChannelSyncStatusService,
|
||||
CalendarEventsImportService,
|
||||
CalendarFetchEventsService,
|
||||
|
||||
+1
-2
@@ -1,13 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
|
||||
import { GoogleCalendarClientProvider } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/providers/google-calendar.provider';
|
||||
import { GoogleCalendarGetEventsService } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/services/google-calendar-get-events.service';
|
||||
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
|
||||
|
||||
@Module({
|
||||
imports: [TwentyConfigModule, OAuth2ClientManagerModule],
|
||||
providers: [GoogleCalendarClientProvider, GoogleCalendarGetEventsService],
|
||||
providers: [GoogleCalendarGetEventsService],
|
||||
exports: [GoogleCalendarGetEventsService],
|
||||
})
|
||||
export class GoogleCalendarDriverModule {}
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type calendar_v3 as calendarV3, google } from 'googleapis';
|
||||
|
||||
import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleCalendarClientProvider {
|
||||
constructor(
|
||||
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
|
||||
) {}
|
||||
|
||||
public async getGoogleCalendarClient(
|
||||
connectedAccount: Pick<
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
'provider' | 'refreshToken'
|
||||
>,
|
||||
): Promise<calendarV3.Calendar> {
|
||||
const oAuth2Client =
|
||||
await this.oAuth2ClientManagerService.getOAuth2Client(connectedAccount);
|
||||
|
||||
const googleCalendarClient = google.calendar({
|
||||
version: 'v3',
|
||||
auth: oAuth2Client,
|
||||
});
|
||||
|
||||
return googleCalendarClient;
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -1,13 +1,14 @@
|
||||
//
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { type GaxiosError } from 'gaxios';
|
||||
import { type calendar_v3 as calendarV3 } from 'googleapis';
|
||||
|
||||
import { GoogleCalendarClientProvider } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/providers/google-calendar.provider';
|
||||
import { formatGoogleCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/format-google-calendar-event.util';
|
||||
import { parseGaxiosError } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/parse-gaxios-error.util';
|
||||
import { parseGoogleCalendarError } from 'src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/parse-google-calendar-error.util';
|
||||
import { type GetCalendarEventsResponse } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-get-events.service';
|
||||
import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@@ -15,7 +16,7 @@ export class GoogleCalendarGetEventsService {
|
||||
private readonly logger = new Logger(GoogleCalendarGetEventsService.name);
|
||||
|
||||
constructor(
|
||||
private readonly googleCalendarClientProvider: GoogleCalendarClientProvider,
|
||||
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
|
||||
) {}
|
||||
|
||||
public async getCalendarEvents(
|
||||
@@ -25,11 +26,13 @@ export class GoogleCalendarGetEventsService {
|
||||
>,
|
||||
syncCursor?: string,
|
||||
): Promise<GetCalendarEventsResponse> {
|
||||
const googleCalendarClient =
|
||||
await this.googleCalendarClientProvider.getGoogleCalendarClient(
|
||||
const oAuth2Client =
|
||||
await this.oAuth2ClientManagerService.getGoogleOAuth2Client(
|
||||
connectedAccount,
|
||||
);
|
||||
|
||||
const googleCalendarClient = oAuth2Client.calendar({ version: 'v3' });
|
||||
|
||||
let nextSyncToken: string | null | undefined;
|
||||
let nextPageToken: string | undefined;
|
||||
const events: calendarV3.Schema$Event[] = [];
|
||||
|
||||
+2
-4
@@ -1,16 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
|
||||
import { MicrosoftCalendarGetEventsService } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/services/microsoft-calendar-get-events.service';
|
||||
import { MicrosoftCalendarImportEventsService } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/services/microsoft-calendar-import-events.service';
|
||||
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';
|
||||
|
||||
@Module({
|
||||
imports: [TwentyConfigModule],
|
||||
imports: [OAuth2ClientManagerModule],
|
||||
providers: [
|
||||
MicrosoftCalendarGetEventsService,
|
||||
MicrosoftCalendarImportEventsService,
|
||||
MicrosoftOAuth2ClientManagerService,
|
||||
],
|
||||
exports: [
|
||||
MicrosoftCalendarGetEventsService,
|
||||
|
||||
+5
-5
@@ -12,27 +12,27 @@ import {
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/drivers/exceptions/calendar-event-import-driver.exception';
|
||||
import { parseMicrosoftCalendarError } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/parse-microsoft-calendar-error.util';
|
||||
import { type GetCalendarEventsResponse } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-get-events.service';
|
||||
import { MicrosoftOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service';
|
||||
import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils';
|
||||
|
||||
@Injectable()
|
||||
export class MicrosoftCalendarGetEventsService {
|
||||
constructor(
|
||||
private readonly microsoftOAuth2ClientManagerService: MicrosoftOAuth2ClientManagerService,
|
||||
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
|
||||
) {}
|
||||
|
||||
public async getCalendarEvents(
|
||||
connectedAccount: Pick<
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
'provider' | 'refreshToken' | 'id'
|
||||
'provider' | 'accessToken' | 'refreshToken' | 'id'
|
||||
>,
|
||||
syncCursor?: string,
|
||||
): Promise<GetCalendarEventsResponse> {
|
||||
try {
|
||||
const microsoftClient =
|
||||
await this.microsoftOAuth2ClientManagerService.getOAuth2Client(
|
||||
connectedAccount.refreshToken,
|
||||
await this.oAuth2ClientManagerService.getMicrosoftOAuth2Client(
|
||||
connectedAccount,
|
||||
);
|
||||
const eventIds: string[] = [];
|
||||
|
||||
|
||||
+5
-5
@@ -9,27 +9,27 @@ import {
|
||||
import { formatMicrosoftCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util';
|
||||
import { parseMicrosoftCalendarError } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/parse-microsoft-calendar-error.util';
|
||||
import { type FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { MicrosoftOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service';
|
||||
import { OAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/services/oauth2-client-manager.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils';
|
||||
|
||||
@Injectable()
|
||||
export class MicrosoftCalendarImportEventsService {
|
||||
constructor(
|
||||
private readonly microsoftOAuth2ClientManagerService: MicrosoftOAuth2ClientManagerService,
|
||||
private readonly oAuth2ClientManagerService: OAuth2ClientManagerService,
|
||||
) {}
|
||||
|
||||
public async getCalendarEvents(
|
||||
connectedAccount: Pick<
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
'provider' | 'refreshToken' | 'id'
|
||||
'provider' | 'accessToken' | 'refreshToken' | 'id'
|
||||
>,
|
||||
changedEventIds: string[],
|
||||
): Promise<FetchedCalendarEvent[]> {
|
||||
try {
|
||||
const microsoftClient =
|
||||
await this.microsoftOAuth2ClientManagerService.getOAuth2Client(
|
||||
connectedAccount.refreshToken,
|
||||
await this.oAuth2ClientManagerService.getMicrosoftOAuth2Client(
|
||||
connectedAccount,
|
||||
);
|
||||
|
||||
const events: Event[] = [];
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
CalendarEventImportDriverException,
|
||||
CalendarEventImportDriverExceptionCode,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/drivers/exceptions/calendar-event-import-driver.exception';
|
||||
import { ConnectedAccountRefreshAccessTokenExceptionCode } from 'src/modules/connected-account/refresh-tokens-manager/exceptions/connected-account-refresh-tokens.exception';
|
||||
import {
|
||||
ConnectedAccountRefreshTokensService,
|
||||
type ConnectedAccountTokens,
|
||||
} from 'src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
interface ValidateAndRefreshConnectedAccountAuthenticationParams {
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity;
|
||||
workspaceId: string;
|
||||
calendarChannelId: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CalendarAccountAuthenticationService {
|
||||
constructor(
|
||||
private readonly connectedAccountRefreshTokensService: ConnectedAccountRefreshTokensService,
|
||||
) {}
|
||||
|
||||
async validateAndRefreshConnectedAccountAuthentication({
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
calendarChannelId: messageChannelId,
|
||||
}: ValidateAndRefreshConnectedAccountAuthenticationParams): Promise<ConnectedAccountTokens> {
|
||||
if (
|
||||
connectedAccount.provider === ConnectedAccountProvider.IMAP_SMTP_CALDAV &&
|
||||
isDefined(connectedAccount.connectionParameters?.SMTP)
|
||||
) {
|
||||
await this.validateCalDavCredentialsForConnectedAccount({
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
calendarChannelId: messageChannelId,
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken: '',
|
||||
refreshToken: '',
|
||||
};
|
||||
}
|
||||
|
||||
return await this.refreshAccessTokenForOAuthProvider({
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
calendarChannelId: messageChannelId,
|
||||
});
|
||||
}
|
||||
|
||||
private async validateCalDavCredentialsForConnectedAccount({
|
||||
connectedAccount,
|
||||
}: ValidateAndRefreshConnectedAccountAuthenticationParams): Promise<void> {
|
||||
if (
|
||||
!isDefined(connectedAccount.connectionParameters) ||
|
||||
!isDefined(connectedAccount.connectionParameters?.CALDAV)
|
||||
) {
|
||||
throw {
|
||||
code: CalendarEventImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
message: 'Missing CALDAV credentials in connectionParameters',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshAccessTokenForOAuthProvider({
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
}: ValidateAndRefreshConnectedAccountAuthenticationParams): Promise<ConnectedAccountTokens> {
|
||||
try {
|
||||
return await this.connectedAccountRefreshTokensService.refreshAndSaveTokens(
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
);
|
||||
} catch (error) {
|
||||
switch (error.code) {
|
||||
case ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR:
|
||||
throw new CalendarEventImportDriverException(
|
||||
error.message,
|
||||
CalendarEventImportDriverExceptionCode.TEMPORARY_ERROR,
|
||||
);
|
||||
case ConnectedAccountRefreshAccessTokenExceptionCode.REFRESH_ACCESS_TOKEN_FAILED:
|
||||
case ConnectedAccountRefreshAccessTokenExceptionCode.REFRESH_TOKEN_NOT_FOUND:
|
||||
throw new CalendarEventImportDriverException(
|
||||
error.message,
|
||||
CalendarEventImportDriverExceptionCode.INSUFFICIENT_PERMISSIONS,
|
||||
);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-1
@@ -8,6 +8,7 @@ import {
|
||||
CalendarEventImportDriverException,
|
||||
CalendarEventImportDriverExceptionCode,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/drivers/exceptions/calendar-event-import-driver.exception';
|
||||
import { CalendarAccountAuthenticationService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-account-authentication.service';
|
||||
import {
|
||||
CalendarEventImportErrorHandlerService,
|
||||
CalendarEventImportSyncStep,
|
||||
@@ -32,6 +33,7 @@ export class CalendarFetchEventsService {
|
||||
private readonly getCalendarEventsService: CalendarGetCalendarEventsService,
|
||||
private readonly calendarEventImportErrorHandlerService: CalendarEventImportErrorHandlerService,
|
||||
private readonly calendarEventsImportService: CalendarEventsImportService,
|
||||
private readonly calendarAccountAuthenticationService: CalendarAccountAuthenticationService,
|
||||
) {}
|
||||
|
||||
public async fetchCalendarEvents(
|
||||
@@ -50,9 +52,24 @@ export class CalendarFetchEventsService {
|
||||
);
|
||||
|
||||
try {
|
||||
const { accessToken, refreshToken } =
|
||||
await this.calendarAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication(
|
||||
{
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
calendarChannelId: calendarChannel.id,
|
||||
},
|
||||
);
|
||||
|
||||
const connectedAccountWithFreshTokens = {
|
||||
...connectedAccount,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
};
|
||||
|
||||
const getCalendarEventsResponse =
|
||||
await this.getCalendarEventsService.getCalendarEvents(
|
||||
connectedAccount,
|
||||
connectedAccountWithFreshTokens,
|
||||
calendarChannel.syncCursor,
|
||||
);
|
||||
|
||||
|
||||
+6
-1
@@ -30,7 +30,12 @@ export class CalendarGetCalendarEventsService {
|
||||
public async getCalendarEvents(
|
||||
connectedAccount: Pick<
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
'provider' | 'refreshToken' | 'id' | 'connectionParameters' | 'handle'
|
||||
| 'provider'
|
||||
| 'accessToken'
|
||||
| 'refreshToken'
|
||||
| 'id'
|
||||
| 'connectionParameters'
|
||||
| 'handle'
|
||||
>,
|
||||
syncCursor?: string,
|
||||
): Promise<GetCalendarEventsResponse> {
|
||||
|
||||
Reference in New Issue
Block a user