import { Injectable } from '@nestjs/common'; import { assertUnreachable } from 'twenty-shared/utils'; import { ConnectedAccountProvider } from 'twenty-shared/types'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { GoogleAPIRefreshAccessTokenService, GoogleTokens, } from 'src/modules/connected-account/refresh-tokens-manager/drivers/google/services/google-api-refresh-access-token.service'; import { MicrosoftAPIRefreshAccessTokenService, MicrosoftTokens, } from 'src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/services/microsoft-api-refresh-tokens.service'; import { ConnectedAccountRefreshAccessTokenException, ConnectedAccountRefreshAccessTokenExceptionCode, } from 'src/modules/connected-account/refresh-tokens-manager/exceptions/connected-account-refresh-tokens.exception'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; export type ConnectedAccountTokens = GoogleTokens | MicrosoftTokens; @Injectable() export class ConnectedAccountRefreshTokensService { constructor( private readonly googleAPIRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService, private readonly microsoftAPIRefreshAccessTokenService: MicrosoftAPIRefreshAccessTokenService, private readonly twentyORMManager: TwentyORMManager, ) {} async refreshAndSaveTokens( connectedAccount: ConnectedAccountWorkspaceEntity, workspaceId: string, ): Promise { const refreshToken = connectedAccount.refreshToken; if (!refreshToken) { throw new ConnectedAccountRefreshAccessTokenException( `No refresh token found for connected account ${connectedAccount.id} in workspace ${workspaceId}`, ConnectedAccountRefreshAccessTokenExceptionCode.REFRESH_TOKEN_NOT_FOUND, ); } const connectedAccountTokens = await this.refreshTokens( connectedAccount, refreshToken, workspaceId, ); try { const connectedAccountRepository = await this.twentyORMManager.getRepository( 'connectedAccount', ); await connectedAccountRepository.update( { id: connectedAccount.id }, connectedAccountTokens, ); } catch (error) { throw new Error( `Error saving the new tokens for connected account ${connectedAccount.id} in workspace ${workspaceId}: ${error.message} `, ); } return connectedAccountTokens.accessToken; } async refreshTokens( connectedAccount: ConnectedAccountWorkspaceEntity, refreshToken: string, workspaceId: string, ): Promise { try { switch (connectedAccount.provider) { case ConnectedAccountProvider.GOOGLE: return this.googleAPIRefreshAccessTokenService.refreshAccessToken( refreshToken, ); case ConnectedAccountProvider.MICROSOFT: return this.microsoftAPIRefreshAccessTokenService.refreshTokens( refreshToken, ); default: return assertUnreachable( connectedAccount.provider, `Provider ${connectedAccount.provider} not supported`, ); } } catch (error) { throw new ConnectedAccountRefreshAccessTokenException( `Error refreshing tokens for connected account ${connectedAccount.id} in workspace ${workspaceId}: ${error.message} ${error?.response?.data?.error_description}`, ConnectedAccountRefreshAccessTokenExceptionCode.REFRESH_ACCESS_TOKEN_FAILED, ); } } }