5899 display a banner to alert users which need to reconnect their account (#6301)
Closes #5899 <img width="1280" alt="Index - banner" src="https://github.com/twentyhq/twenty/assets/71827178/313cf20d-eb34-496a-8c7c-7589fbd55954"> --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
@ -51,6 +52,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
BillingModule,
|
||||
RefreshAccessTokenManagerModule,
|
||||
CalendarEventParticipantManagerModule,
|
||||
UserVarsModule,
|
||||
],
|
||||
providers: [
|
||||
CalendarChannelSyncStatusService,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
|
||||
import { CacheStorageService } from 'src/engine/integrations/cache-storage/cache-storage.service';
|
||||
import { InjectCacheStorage } from 'src/engine/integrations/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageNamespace } from 'src/engine/integrations/cache-storage/types/cache-storage-namespace.enum';
|
||||
@ -9,6 +10,10 @@ import {
|
||||
CalendarChannelSyncStatus,
|
||||
CalendarChannelWorkspaceEntity,
|
||||
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import {
|
||||
ConnectedAccountKeyValueType,
|
||||
ConnectedAccountKeys,
|
||||
} from 'src/modules/connected-account/types/connected-account-key-value.type';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarChannelSyncStatusService {
|
||||
@ -16,6 +21,7 @@ export class CalendarChannelSyncStatusService {
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
@InjectCacheStorage(CacheStorageNamespace.Calendar)
|
||||
private readonly cacheStorage: CacheStorageService,
|
||||
private readonly userVarsService: UserVarsService<ConnectedAccountKeyValueType>,
|
||||
) {}
|
||||
|
||||
public async scheduleFullCalendarEventListFetch(calendarChannelId: string) {
|
||||
@ -157,5 +163,55 @@ export class CalendarChannelSyncStatusService {
|
||||
syncStatus: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS,
|
||||
syncStage: CalendarChannelSyncStage.FAILED,
|
||||
});
|
||||
|
||||
await this.addToAccountsToReconnect(calendarChannelId, workspaceId);
|
||||
}
|
||||
|
||||
private async addToAccountsToReconnect(
|
||||
calendarChannelId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
const calendarChannel = await calendarChannelRepository.findOne({
|
||||
where: {
|
||||
id: calendarChannelId,
|
||||
},
|
||||
relations: {
|
||||
connectedAccount: {
|
||||
accountOwner: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!calendarChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = calendarChannel.connectedAccount.accountOwner.userId;
|
||||
const connectedAccountId = calendarChannel.connectedAccount.id;
|
||||
|
||||
const accountsToReconnect =
|
||||
(await this.userVarsService.get({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
})) ?? [];
|
||||
|
||||
if (accountsToReconnect.includes(connectedAccountId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
accountsToReconnect.push(connectedAccountId);
|
||||
|
||||
await this.userVarsService.set({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
value: accountsToReconnect,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
|
||||
import { ConnectedAccountListener } from 'src/modules/connected-account/listeners/connected-account.listener';
|
||||
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
|
||||
|
||||
@Module({
|
||||
imports: [UserVarsModule],
|
||||
providers: [AccountsToReconnectService, ConnectedAccountListener],
|
||||
exports: [AccountsToReconnectService],
|
||||
})
|
||||
export class ConnectedAccountModule {}
|
||||
@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class ConnectedAccountListener {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly accountsToReconnectService: AccountsToReconnectService,
|
||||
) {}
|
||||
|
||||
@OnEvent('connectedAccount.deleted')
|
||||
async handleDeletedEvent(
|
||||
payload: ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity>,
|
||||
) {
|
||||
const workspaceMemberId = payload.properties.before.accountOwnerId;
|
||||
const workspaceId = payload.workspaceId;
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
);
|
||||
const workspaceMember = await workspaceMemberRepository.findOneOrFail({
|
||||
where: { id: workspaceMemberId },
|
||||
});
|
||||
|
||||
const userId = workspaceMember.userId;
|
||||
|
||||
const connectedAccountId = payload.properties.before.id;
|
||||
|
||||
await this.accountsToReconnectService.removeAccountToReconnect(
|
||||
userId,
|
||||
workspaceId,
|
||||
connectedAccountId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
|
||||
import {
|
||||
ConnectedAccountKeys,
|
||||
ConnectedAccountKeyValueType,
|
||||
} from 'src/modules/connected-account/types/connected-account-key-value.type';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsToReconnectService {
|
||||
constructor(
|
||||
private readonly userVarsService: UserVarsService<ConnectedAccountKeyValueType>,
|
||||
) {}
|
||||
|
||||
public async removeAccountToReconnect(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
connectedAccountId: string,
|
||||
) {
|
||||
const accountsToReconnect = await this.userVarsService.get({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
});
|
||||
|
||||
if (!accountsToReconnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedAccountsToReconnect = accountsToReconnect.filter(
|
||||
(id) => id !== connectedAccountId,
|
||||
);
|
||||
|
||||
if (updatedAccountsToReconnect.length === 0) {
|
||||
await this.userVarsService.delete({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.userVarsService.set({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
value: updatedAccountsToReconnect,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
export enum ConnectedAccountKeys {
|
||||
ACCOUNTS_TO_RECONNECT = 'ACCOUNTS_TO_RECONNECT',
|
||||
}
|
||||
|
||||
export type ConnectedAccountKeyValueType = {
|
||||
[ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT]: string[];
|
||||
};
|
||||
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { MessagingChannelSyncStatusService } from 'src/modules/messaging/common/services/messaging-channel-sync-status.service';
|
||||
@ -20,6 +21,7 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
MessageThreadWorkspaceEntity,
|
||||
]),
|
||||
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
|
||||
UserVarsModule,
|
||||
],
|
||||
providers: [MessagingChannelSyncStatusService],
|
||||
exports: [MessagingChannelSyncStatusService],
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
|
||||
import { CacheStorageService } from 'src/engine/integrations/cache-storage/cache-storage.service';
|
||||
import { InjectCacheStorage } from 'src/engine/integrations/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageNamespace } from 'src/engine/integrations/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import {
|
||||
ConnectedAccountKeys,
|
||||
ConnectedAccountKeyValueType,
|
||||
} from 'src/modules/connected-account/types/connected-account-key-value.type';
|
||||
import { MessageChannelRepository } from 'src/modules/messaging/common/repositories/message-channel.repository';
|
||||
import {
|
||||
MessageChannelWorkspaceEntity,
|
||||
MessageChannelSyncStage,
|
||||
MessageChannelSyncStatus,
|
||||
MessageChannelWorkspaceEntity,
|
||||
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@ -18,6 +24,8 @@ export class MessagingChannelSyncStatusService {
|
||||
private readonly messageChannelRepository: MessageChannelRepository,
|
||||
@InjectCacheStorage(CacheStorageNamespace.Messaging)
|
||||
private readonly cacheStorage: CacheStorageService,
|
||||
private readonly userVarsService: UserVarsService<ConnectedAccountKeyValueType>,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
) {}
|
||||
|
||||
public async scheduleFullMessageListFetch(
|
||||
@ -160,5 +168,55 @@ export class MessagingChannelSyncStatusService {
|
||||
MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.addToAccountsToReconnect(messageChannelId, workspaceId);
|
||||
}
|
||||
|
||||
private async addToAccountsToReconnect(
|
||||
messageChannelId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
const messageChannel = await messageChannelRepository.findOne({
|
||||
where: {
|
||||
id: messageChannelId,
|
||||
},
|
||||
relations: {
|
||||
connectedAccount: {
|
||||
accountOwner: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!messageChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = messageChannel.connectedAccount.accountOwner.userId;
|
||||
const connectedAccountId = messageChannel.connectedAccount.id;
|
||||
|
||||
const accountsToReconnect =
|
||||
(await this.userVarsService.get({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
})) ?? [];
|
||||
|
||||
if (accountsToReconnect.includes(connectedAccountId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
accountsToReconnect.push(connectedAccountId);
|
||||
|
||||
await this.userVarsService.set({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: ConnectedAccountKeys.ACCOUNTS_TO_RECONNECT,
|
||||
value: accountsToReconnect,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CalendarModule } from 'src/modules/calendar/calendar.module';
|
||||
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
|
||||
import { MessagingModule } from 'src/modules/messaging/messaging.module';
|
||||
import { ViewModule } from 'src/modules/view/view.module';
|
||||
|
||||
@Module({
|
||||
imports: [MessagingModule, CalendarModule, ViewModule],
|
||||
imports: [
|
||||
MessagingModule,
|
||||
CalendarModule,
|
||||
ConnectedAccountModule,
|
||||
ViewModule,
|
||||
],
|
||||
providers: [],
|
||||
exports: [],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user