[calendar/messaging] fix connected account auth failed should skip sync (#4920)
- AuthFailedAt is set when a refreshToken is not valid and an accessToken can't be generated, meaning it will need a manual action from the user to provide a new refresh token. - Calendar/messaging jobs should not be executed if authFailedAt is not null.
This commit is contained in:
@ -43,6 +43,75 @@ export class ConnectedAccountRepository {
|
||||
);
|
||||
}
|
||||
|
||||
public async getAllByHandleAndWorkspaceMemberId(
|
||||
handle: string,
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<ConnectedAccountObjectMetadata>[] | undefined> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const connectedAccounts =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."connectedAccount" WHERE "handle" = $1 AND "accountOwnerId" = $2 LIMIT 1`,
|
||||
[handle, workspaceMemberId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return connectedAccounts;
|
||||
}
|
||||
|
||||
public async create(
|
||||
connectedAccount: Pick<
|
||||
ObjectRecord<ConnectedAccountObjectMetadata>,
|
||||
| 'id'
|
||||
| 'handle'
|
||||
| 'provider'
|
||||
| 'accessToken'
|
||||
| 'refreshToken'
|
||||
| 'accountOwnerId'
|
||||
>,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<ConnectedAccountObjectMetadata>> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."connectedAccount" ("id", "handle", "provider", "accessToken", "refreshToken", "accountOwnerId") VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[
|
||||
connectedAccount.id,
|
||||
connectedAccount.handle,
|
||||
connectedAccount.provider,
|
||||
connectedAccount.accessToken,
|
||||
connectedAccount.refreshToken,
|
||||
connectedAccount.accountOwnerId,
|
||||
],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateAccessTokenAndRefreshToken(
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."connectedAccount" SET "accessToken" = $1, "refreshToken" = $2 WHERE "id" = $3`,
|
||||
[accessToken, refreshToken, connectedAccountId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getById(
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
|
||||
@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
|
||||
import axios from 'axios';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||
import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
|
||||
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
|
||||
@ -14,7 +13,6 @@ export class GoogleAPIRefreshAccessTokenService {
|
||||
private readonly environmentService: EnvironmentService,
|
||||
@InjectObjectMetadataRepository(ConnectedAccountObjectMetadata)
|
||||
private readonly connectedAccountRepository: ConnectedAccountRepository,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
|
||||
async refreshAndSaveAccessToken(
|
||||
@ -32,6 +30,12 @@ export class GoogleAPIRefreshAccessTokenService {
|
||||
);
|
||||
}
|
||||
|
||||
if (connectedAccount.authFailedAt) {
|
||||
throw new Error(
|
||||
`Skipping refresh of access token for connected account ${connectedAccountId} in workspace ${workspaceId} because auth already failed, a new refresh token is needed`,
|
||||
);
|
||||
}
|
||||
|
||||
const refreshToken = connectedAccount.refreshToken;
|
||||
|
||||
if (!refreshToken) {
|
||||
@ -82,11 +86,7 @@ export class GoogleAPIRefreshAccessTokenService {
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
this.exceptionHandlerService.captureExceptions([error], {
|
||||
user: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
throw new Error(`Error refreshing access token: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ import { CalendarChannelObjectMetadata } from 'src/modules/calendar/standard-obj
|
||||
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
|
||||
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
||||
|
||||
export enum ConnectedAccountProvider {
|
||||
GOOGLE = 'google',
|
||||
}
|
||||
|
||||
@ObjectMetadata({
|
||||
standardId: standardObjectIds.connectedAccount,
|
||||
namePlural: 'connectedAccounts',
|
||||
@ -43,7 +47,7 @@ export class ConnectedAccountObjectMetadata extends BaseObjectMetadata {
|
||||
description: 'The account provider',
|
||||
icon: 'IconSettings',
|
||||
})
|
||||
provider: string;
|
||||
provider: ConnectedAccountProvider; // field metadata should be a SELECT
|
||||
|
||||
@FieldMetadata({
|
||||
standardId: connectedAccountStandardFieldIds.accessToken,
|
||||
|
||||
Reference in New Issue
Block a user