Refactor global datasource part 3 (#16447)

## Context
Following https://github.com/twentyhq/twenty/pull/16399
Now using the new global orm manager everywhere and returning a
GlobalDatasource/WorkspaceDatasource based on a feature flag.
This means we now need to wrap all our ORM calls within
executeInWorkspaceContext callback (at least for now) so the global
datasource can dynamically hydrate its context via the new store (the
global datasource does not store anything related to workspaces as it is
now a unique singleton). If feature flag is off it still uses local data
stored in the workspace datasource.
This commit is contained in:
Weiko
2025-12-10 17:17:33 +01:00
committed by GitHub
parent 4f13022774
commit 9bd8f94b3a
203 changed files with 8887 additions and 7237 deletions
@@ -2,7 +2,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { type MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
@@ -42,20 +42,21 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
findOneByOrFail: jest.fn(),
};
const mockTwentyORMGlobalManager = {
getRepositoryForWorkspace: jest
const mockGlobalWorkspaceOrmManager = {
getRepository: jest.fn().mockImplementation((workspaceId, name) => {
if (name === 'messageChannelMessageAssociation') {
return mockMessageChannelMessageAssociationRepository;
}
if (name === 'connectedAccount') {
return mockConnectedAccountRepository;
}
if (name === 'workspaceMember') {
return mockWorkspaceMemberRepository;
}
}),
executeInWorkspaceContext: jest
.fn()
.mockImplementation((workspaceId, name) => {
if (name === 'messageChannelMessageAssociation') {
return mockMessageChannelMessageAssociationRepository;
}
if (name === 'connectedAccount') {
return mockConnectedAccountRepository;
}
if (name === 'workspaceMember') {
return mockWorkspaceMemberRepository;
}
}),
.mockImplementation((_authContext: any, fn: () => any) => fn()),
};
beforeEach(async () => {
@@ -63,8 +64,8 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
providers: [
ApplyMessagesVisibilityRestrictionsService,
{
provide: TwentyORMGlobalManager,
useValue: mockTwentyORMGlobalManager,
provide: GlobalWorkspaceOrmManager,
useValue: mockGlobalWorkspaceOrmManager,
},
],
}).compile();
@@ -6,7 +6,8 @@ import { isDefined } from 'twenty-shared/utils';
import { In } from 'typeorm';
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
@@ -16,105 +17,117 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
@Injectable()
export class ApplyMessagesVisibilityRestrictionsService {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
) {}
public async applyMessagesVisibilityRestrictions(
messages: MessageWorkspaceEntity[],
workspaceId: string,
userId?: string, // undefined when request is made with api key
userId?: string,
) {
const messageChannelMessageAssociationRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
workspaceId,
'messageChannelMessageAssociation',
);
const authContext = buildSystemAuthContext(workspaceId);
const messageChannelMessagesAssociations =
await messageChannelMessageAssociationRepository.find({
where: {
messageId: In(messages.map((message) => message.id)),
},
relations: ['messageChannel'],
});
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelMessageAssociationRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
workspaceId,
'messageChannelMessageAssociation',
);
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
for (let i = messages.length - 1; i >= 0; i--) {
const messageChannelMessageAssociations =
messageChannelMessagesAssociations.filter(
(association) => association.messageId === messages[i].id,
);
const messageChannels = messageChannelMessageAssociations
.map((association) => association.messageChannel)
.filter(
(channel): channel is NonNullable<typeof channel> => channel !== null,
);
if (messageChannels.length === 0) {
throw new NotFoundError('Associated message channels not found');
}
const messageChannelsGroupByVisibility = groupBy(
messageChannels,
(channel) => channel.visibility,
);
if (
messageChannelsGroupByVisibility[
MessageChannelVisibility.SHARE_EVERYTHING
]
) {
continue;
}
if (isDefined(userId)) {
const workspaceMember = await workspaceMemberRepository.findOneByOrFail(
{
userId,
},
);
const connectedAccounts = await connectedAccountRepository.find({
select: ['id'],
where: {
messageChannels: {
id: In(messageChannels.map((channel) => channel.id)),
const messageChannelMessagesAssociations =
await messageChannelMessageAssociationRepository.find({
where: {
messageId: In(messages.map((message) => message.id)),
},
accountOwnerId: workspaceMember.id,
},
});
relations: ['messageChannel'],
});
if (connectedAccounts.length > 0) {
continue;
const connectedAccountRepository =
await this.globalWorkspaceOrmManager.getRepository<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const workspaceMemberRepository =
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
for (let i = messages.length - 1; i >= 0; i--) {
const messageChannelMessageAssociations =
messageChannelMessagesAssociations.filter(
(association) => association.messageId === messages[i].id,
);
const messageChannels = messageChannelMessageAssociations
.map((association) => association.messageChannel)
.filter(
(channel): channel is NonNullable<typeof channel> =>
channel !== null,
);
if (messageChannels.length === 0) {
throw new NotFoundError('Associated message channels not found');
}
const messageChannelsGroupByVisibility = groupBy(
messageChannels,
(channel) => channel.visibility,
);
if (
messageChannelsGroupByVisibility[
MessageChannelVisibility.SHARE_EVERYTHING
]
) {
continue;
}
if (isDefined(userId)) {
const workspaceMember =
await workspaceMemberRepository.findOneByOrFail({
userId,
});
const connectedAccounts = await connectedAccountRepository.find({
select: ['id'],
where: {
messageChannels: {
id: In(messageChannels.map((channel) => channel.id)),
},
accountOwnerId: workspaceMember.id,
},
});
if (connectedAccounts.length > 0) {
continue;
}
}
if (
messageChannelsGroupByVisibility[MessageChannelVisibility.SUBJECT]
) {
messages[i].text = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
if (
messageChannelsGroupByVisibility[MessageChannelVisibility.METADATA]
) {
messages[i].subject =
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
messages[i].text = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
messages.splice(i, 1);
}
}
if (messageChannelsGroupByVisibility[MessageChannelVisibility.SUBJECT]) {
messages[i].text = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
if (messageChannelsGroupByVisibility[MessageChannelVisibility.METADATA]) {
messages[i].subject = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
messages[i].text = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
messages.splice(i, 1);
}
return messages;
return messages;
},
);
}
}
@@ -14,7 +14,8 @@ import {
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import {
MessageChannelPendingGroupEmailsAction,
MessageChannelSyncStage,
@@ -39,7 +40,7 @@ export class MessageChannelUpdateOnePreQueryHook
);
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly messagingProcessGroupEmailActionsService: MessagingProcessGroupEmailActionsService,
) {}
@@ -52,89 +53,97 @@ export class MessageChannelUpdateOnePreQueryHook
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspace.id,
'messageChannel',
);
const systemAuthContext = buildSystemAuthContext(workspace.id);
const messageChannel = await messageChannelRepository.findOne({
where: { id: payload.id },
});
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
systemAuthContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspace.id,
'messageChannel',
);
if (!isDefined(messageChannel)) {
throw new WorkspaceQueryRunnerException(
'Message channel not found',
WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND,
{
userFriendlyMessage: msg`Message channel not found`,
},
);
}
const messageChannel = await messageChannelRepository.findOne({
where: { id: payload.id },
});
const isSyncOngoing = ONGOING_SYNC_STAGES.includes(
messageChannel.syncStage,
if (!isDefined(messageChannel)) {
throw new WorkspaceQueryRunnerException(
'Message channel not found',
WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND,
{
userFriendlyMessage: msg`Message channel not found`,
},
);
}
const isSyncOngoing = ONGOING_SYNC_STAGES.includes(
messageChannel.syncStage,
);
const messageFolderRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageFolderWorkspaceEntity>(
workspace.id,
'messageFolder',
);
const messageFoldersWithPendingActionCount =
await messageFolderRepository.count({
where: {
messageChannelId: messageChannel.id,
pendingSyncAction: Not(MessageFolderPendingSyncAction.NONE),
},
});
const hasPendingFolderActions =
messageFoldersWithPendingActionCount > 0;
const hasPendingGroupEmailsAction =
messageChannel.pendingGroupEmailsAction !==
MessageChannelPendingGroupEmailsAction.NONE;
if (
isSyncOngoing &&
(hasPendingFolderActions || hasPendingGroupEmailsAction)
) {
throw new WorkspaceQueryRunnerException(
'Cannot update message channel while sync is ongoing with pending actions',
WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
{
userFriendlyMessage: msg`Cannot update message channel while sync is ongoing. Please wait for the sync to complete.`,
},
);
}
const hasCompletedConfiguration =
messageChannel.syncStage !==
MessageChannelSyncStage.PENDING_CONFIGURATION;
if (!hasCompletedConfiguration) {
this.logger.log(
`MessageChannelId: ${messageChannel.id} - Skipping pending action for message channel in PENDING_CONFIGURATION state`,
);
return payload;
}
const excludeGroupEmailsChanged =
isDefined(payload.data.excludeGroupEmails) &&
payload.data.excludeGroupEmails !== messageChannel.excludeGroupEmails;
if (excludeGroupEmailsChanged) {
await this.messagingProcessGroupEmailActionsService.markMessageChannelAsPendingGroupEmailsAction(
messageChannel,
workspace.id,
payload.data.excludeGroupEmails
? MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_DELETION
: MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_IMPORT,
);
}
return payload;
},
);
const messageFolderRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
workspace.id,
'messageFolder',
);
const messageFoldersWithPendingActionCount =
await messageFolderRepository.count({
where: {
messageChannelId: messageChannel.id,
pendingSyncAction: Not(MessageFolderPendingSyncAction.NONE),
},
});
const hasPendingFolderActions = messageFoldersWithPendingActionCount > 0;
const hasPendingGroupEmailsAction =
messageChannel.pendingGroupEmailsAction !==
MessageChannelPendingGroupEmailsAction.NONE;
if (
isSyncOngoing &&
(hasPendingFolderActions || hasPendingGroupEmailsAction)
) {
throw new WorkspaceQueryRunnerException(
'Cannot update message channel while sync is ongoing with pending actions',
WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
{
userFriendlyMessage: msg`Cannot update message channel while sync is ongoing. Please wait for the sync to complete.`,
},
);
}
const hasCompletedConfiguration =
messageChannel.syncStage !==
MessageChannelSyncStage.PENDING_CONFIGURATION;
if (!hasCompletedConfiguration) {
this.logger.log(
`MessageChannelId: ${messageChannel.id} - Skipping pending action for message channel in PENDING_CONFIGURATION state`,
);
return payload;
}
const excludeGroupEmailsChanged =
isDefined(payload.data.excludeGroupEmails) &&
payload.data.excludeGroupEmails !== messageChannel.excludeGroupEmails;
if (excludeGroupEmailsChanged) {
await this.messagingProcessGroupEmailActionsService.markMessageChannelAsPendingGroupEmailsAction(
messageChannel,
workspace.id,
payload.data.excludeGroupEmails
? MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_DELETION
: MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_IMPORT,
);
}
return payload;
}
}
@@ -7,7 +7,8 @@ import { CacheStorageService } from 'src/engine/core-modules/cache-storage/servi
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type';
@@ -27,7 +28,7 @@ export class MessageChannelSyncStatusService {
constructor(
@InjectCacheStorage(CacheStorageNamespace.ModuleMessaging)
private readonly cacheStorage: CacheStorageService,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly accountsToReconnectService: AccountsToReconnectService,
private readonly metricsService: MetricsService,
) {}
@@ -41,16 +42,23 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
},
);
}
public async markAsMessagesImportPending(
@@ -62,16 +70,23 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
},
);
}
public async resetAndMarkAsMessagesListFetchPending(
@@ -88,30 +103,37 @@ export class MessageChannelSyncStatusService {
);
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
const messageFolderRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
workspaceId,
'messageFolder',
);
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncCursor: '',
syncStageStartedAt: null,
throttleFailureCount: 0,
pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction.NONE,
});
const messageFolderRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageFolderWorkspaceEntity>(
workspaceId,
'messageFolder',
);
await messageFolderRepository.update(
{ messageChannelId: In(messageChannelIds) },
{
syncCursor: '',
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
await messageChannelRepository.update(messageChannelIds, {
syncCursor: '',
syncStageStartedAt: null,
throttleFailureCount: 0,
pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction.NONE,
});
await messageFolderRepository.update(
{ messageChannelId: In(messageChannelIds) },
{
syncCursor: '',
pendingSyncAction: MessageFolderPendingSyncAction.NONE,
},
);
},
);
@@ -126,15 +148,22 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStageStartedAt: null,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStageStartedAt: null,
});
},
);
}
public async markAsMessagesListFetchScheduled(
@@ -145,17 +174,24 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
},
);
}
public async markAsMessagesListFetchOngoing(
@@ -166,16 +202,23 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING,
syncStatus: MessageChannelSyncStatus.ONGOING,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING,
syncStatus: MessageChannelSyncStatus.ONGOING,
});
},
);
}
public async markAsCompletedAndMarkAsMessagesListFetchPending(
@@ -186,19 +229,26 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStatus: MessageChannelSyncStatus.ACTIVE,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
throttleFailureCount: 0,
syncStageStartedAt: null,
syncedAt: new Date().toISOString(),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStatus: MessageChannelSyncStatus.ACTIVE,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
throttleFailureCount: 0,
syncStageStartedAt: null,
syncedAt: new Date().toISOString(),
});
},
);
await this.metricsService.batchIncrementCounter({
key: MetricsKeys.MessageChannelSyncJobActive,
@@ -214,15 +264,22 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED,
});
},
);
}
public async markAsMessagesImportOngoing(
@@ -233,16 +290,23 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
},
);
}
public async markAsFailed(
@@ -256,57 +320,66 @@ export class MessageChannelSyncStatusService {
return;
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.FAILED,
syncStatus: syncStatus,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const messageChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);
const metricsKey =
syncStatus === MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS
? MetricsKeys.MessageChannelSyncJobFailedInsufficientPermissions
: MetricsKeys.MessageChannelSyncJobFailedUnknown;
await messageChannelRepository.update(messageChannelIds, {
syncStage: MessageChannelSyncStage.FAILED,
syncStatus: syncStatus,
});
await this.metricsService.batchIncrementCounter({
key: metricsKey,
eventIds: messageChannelIds,
});
const metricsKey =
syncStatus ===
MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS
? MetricsKeys.MessageChannelSyncJobFailedInsufficientPermissions
: MetricsKeys.MessageChannelSyncJobFailedUnknown;
if (
syncStatus === MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS
) {
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
await this.metricsService.batchIncrementCounter({
key: metricsKey,
eventIds: messageChannelIds,
});
const messageChannels = await messageChannelRepository.find({
select: ['id', 'connectedAccountId'],
where: { id: Any(messageChannelIds) },
});
if (
syncStatus ===
MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS
) {
const connectedAccountRepository =
await this.globalWorkspaceOrmManager.getRepository<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const connectedAccountIds = messageChannels.map(
(messageChannel) => messageChannel.connectedAccountId,
);
const messageChannels = await messageChannelRepository.find({
select: ['id', 'connectedAccountId'],
where: { id: Any(messageChannelIds) },
});
await connectedAccountRepository.update(
{ id: Any(connectedAccountIds) },
{
authFailedAt: new Date(),
},
);
const connectedAccountIds = messageChannels.map(
(messageChannel) => messageChannel.connectedAccountId,
);
await this.addToAccountsToReconnect(
messageChannels.map((messageChannel) => messageChannel.id),
workspaceId,
);
}
await connectedAccountRepository.update(
{ id: Any(connectedAccountIds) },
{
authFailedAt: new Date(),
},
);
await this.addToAccountsToReconnect(
messageChannels.map((messageChannel) => messageChannel.id),
workspaceId,
);
}
},
);
}
private async addToAccountsToReconnect(
@@ -318,7 +391,7 @@ export class MessageChannelSyncStatusService {
}
const messageChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
await this.globalWorkspaceOrmManager.getRepository<MessageChannelWorkspaceEntity>(
workspaceId,
'messageChannel',
);