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 { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
@@ -52,20 +52,21 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
findOneByOrFail: jest.fn(),
};
const mockTwentyORMGlobalManager = {
getRepositoryForWorkspace: jest
const mockGlobalWorkspaceOrmManager = {
getRepository: jest.fn().mockImplementation((workspaceId, name) => {
if (name === 'calendarChannelEventAssociation') {
return mockCalendarEventAssociationRepository;
}
if (name === 'connectedAccount') {
return mockConnectedAccountRepository;
}
if (name === 'workspaceMember') {
return mockWorkspaceMemberRepository;
}
}),
executeInWorkspaceContext: jest
.fn()
.mockImplementation((workspaceId, name) => {
if (name === 'calendarChannelEventAssociation') {
return mockCalendarEventAssociationRepository;
}
if (name === 'connectedAccount') {
return mockConnectedAccountRepository;
}
if (name === 'workspaceMember') {
return mockWorkspaceMemberRepository;
}
}),
.mockImplementation((_authContext: any, fn: () => any) => fn()),
};
beforeEach(async () => {
@@ -73,8 +74,8 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
providers: [
ApplyCalendarEventsVisibilityRestrictionsService,
{
provide: TwentyORMGlobalManager,
useValue: mockTwentyORMGlobalManager,
provide: GlobalWorkspaceOrmManager,
useValue: mockGlobalWorkspaceOrmManager,
},
],
}).compile();
@@ -5,7 +5,8 @@ import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/
import { isDefined } from 'twenty-shared/utils';
import { In } from 'typeorm';
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 CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
@@ -15,7 +16,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
@Injectable()
export class ApplyCalendarEventsVisibilityRestrictionsService {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
) {}
public async applyCalendarEventsVisibilityRestrictions(
@@ -23,90 +24,99 @@ export class ApplyCalendarEventsVisibilityRestrictionsService {
workspaceId: string,
userId?: string, // undefined when request is made with api key
) {
const calendarChannelEventAssociationRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelEventAssociationWorkspaceEntity>(
workspaceId,
'calendarChannelEventAssociation',
);
const authContext = buildSystemAuthContext(workspaceId);
const calendarChannelCalendarEventsAssociations =
await calendarChannelEventAssociationRepository.find({
where: {
calendarEventId: In(calendarEvents.map((event) => event.id)),
},
relations: ['calendarChannel'],
});
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelEventAssociationRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
workspaceId,
'calendarChannelEventAssociation',
);
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
for (let i = calendarEvents.length - 1; i >= 0; i--) {
const calendarChannelCalendarEventAssociations =
calendarChannelCalendarEventsAssociations.filter(
(association) => association.calendarEventId === calendarEvents[i].id,
);
const calendarChannels = calendarChannelCalendarEventAssociations.map(
(association) => association.calendarChannel,
);
const calendarChannelsGroupByVisibility = groupBy(
calendarChannels,
(channel) => channel.visibility,
);
if (
calendarChannelsGroupByVisibility[
CalendarChannelVisibility.SHARE_EVERYTHING
]
) {
continue;
}
if (isDefined(userId)) {
const workspaceMember = await workspaceMemberRepository.findOneByOrFail(
{
userId: userId,
},
);
const connectedAccounts = await connectedAccountRepository.find({
select: ['id'],
where: {
calendarChannels: {
id: In(calendarChannels.map((channel) => channel.id)),
const calendarChannelCalendarEventsAssociations =
await calendarChannelEventAssociationRepository.find({
where: {
calendarEventId: In(calendarEvents.map((event) => event.id)),
},
accountOwnerId: workspaceMember.id,
},
});
relations: ['calendarChannel'],
});
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 = calendarEvents.length - 1; i >= 0; i--) {
const calendarChannelCalendarEventAssociations =
calendarChannelCalendarEventsAssociations.filter(
(association) =>
association.calendarEventId === calendarEvents[i].id,
);
const calendarChannels = calendarChannelCalendarEventAssociations.map(
(association) => association.calendarChannel,
);
const calendarChannelsGroupByVisibility = groupBy(
calendarChannels,
(channel) => channel.visibility,
);
if (
calendarChannelsGroupByVisibility[
CalendarChannelVisibility.SHARE_EVERYTHING
]
) {
continue;
}
if (isDefined(userId)) {
const workspaceMember =
await workspaceMemberRepository.findOneByOrFail({
userId: userId,
});
const connectedAccounts = await connectedAccountRepository.find({
select: ['id'],
where: {
calendarChannels: {
id: In(calendarChannels.map((channel) => channel.id)),
},
accountOwnerId: workspaceMember.id,
},
});
if (connectedAccounts.length > 0) {
continue;
}
}
if (
calendarChannelsGroupByVisibility[
CalendarChannelVisibility.METADATA
]
) {
calendarEvents[i].title =
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
calendarEvents[i].description =
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
calendarEvents.splice(i, 1);
}
}
if (
calendarChannelsGroupByVisibility[CalendarChannelVisibility.METADATA]
) {
calendarEvents[i].title =
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
calendarEvents[i].description =
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
continue;
}
calendarEvents.splice(i, 1);
}
return calendarEvents;
return calendarEvents;
},
);
}
}
@@ -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 {
CalendarChannelSyncStage,
CalendarChannelSyncStatus,
@@ -20,7 +21,7 @@ import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/acc
@Injectable()
export class CalendarChannelSyncStatusService {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
@InjectCacheStorage(CacheStorageNamespace.ModuleCalendar)
private readonly cacheStorage: CacheStorageService,
private readonly accountsToReconnectService: AccountsToReconnectService,
@@ -36,16 +37,23 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
},
);
}
public async markAsCalendarEventListFetchOngoing(
@@ -56,17 +64,24 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING,
syncStatus: CalendarChannelSyncStatus.ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING,
syncStatus: CalendarChannelSyncStatus.ONGOING,
syncStageStartedAt: new Date().toISOString(),
});
},
);
}
public async resetAndMarkAsCalendarEventListFetchPending(
@@ -83,17 +98,24 @@ export class CalendarChannelSyncStatusService {
);
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncCursor: '',
syncStageStartedAt: null,
throttleFailureCount: 0,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncCursor: '',
syncStageStartedAt: null,
throttleFailureCount: 0,
});
},
);
await this.markAsCalendarEventListFetchPending(
calendarChannelIds,
@@ -109,15 +131,22 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStageStartedAt: null,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStageStartedAt: null,
});
},
);
}
public async markAsCalendarEventsImportPending(
@@ -129,16 +158,23 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING,
...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}),
});
},
);
}
public async markAsCalendarEventsImportOngoing(
@@ -149,16 +185,23 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING,
syncStatus: CalendarChannelSyncStatus.ONGOING,
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING,
syncStatus: CalendarChannelSyncStatus.ONGOING,
});
},
);
}
public async markAsCompletedAndMarkAsCalendarEventListFetchPending(
@@ -169,19 +212,26 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const authContext = buildSystemAuthContext(workspaceId);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING,
syncStatus: CalendarChannelSyncStatus.ACTIVE,
throttleFailureCount: 0,
syncStageStartedAt: null,
syncedAt: new Date().toISOString(),
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING,
syncStatus: CalendarChannelSyncStatus.ACTIVE,
throttleFailureCount: 0,
syncStageStartedAt: null,
syncedAt: new Date().toISOString(),
});
},
);
await this.markAsCalendarEventListFetchPending(
calendarChannelIds,
@@ -202,22 +252,29 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
for (const calendarChannelId of calendarChannelIds) {
await this.cacheStorage.del(
`calendar-events-to-import:${workspaceId}:${calendarChannelId}`,
);
}
await calendarChannelRepository.update(calendarChannelIds, {
syncStatus: CalendarChannelSyncStatus.FAILED_UNKNOWN,
syncStage: CalendarChannelSyncStage.FAILED,
});
const authContext = buildSystemAuthContext(workspaceId);
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStatus: CalendarChannelSyncStatus.FAILED_UNKNOWN,
syncStage: CalendarChannelSyncStage.FAILED,
});
},
);
await this.metricsService.batchIncrementCounter({
key: MetricsKeys.CalendarEventSyncJobFailedUnknown,
@@ -233,49 +290,57 @@ export class CalendarChannelSyncStatusService {
return;
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
for (const calendarChannelId of calendarChannelIds) {
await this.cacheStorage.del(
`calendar-events-to-import:${workspaceId}:${calendarChannelId}`,
);
}
await calendarChannelRepository.update(calendarChannelIds, {
syncStatus: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS,
syncStage: CalendarChannelSyncStage.FAILED,
});
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const authContext = buildSystemAuthContext(workspaceId);
const calendarChannels = await calendarChannelRepository.find({
select: ['id', 'connectedAccountId'],
where: { id: Any(calendarChannelIds) },
});
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () => {
const calendarChannelRepository =
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);
const connectedAccountIds = calendarChannels.map(
(calendarChannel) => calendarChannel.connectedAccountId,
);
await calendarChannelRepository.update(calendarChannelIds, {
syncStatus: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS,
syncStage: CalendarChannelSyncStage.FAILED,
});
await connectedAccountRepository.update(
{ id: Any(connectedAccountIds) },
{
authFailedAt: new Date(),
const connectedAccountRepository =
await this.globalWorkspaceOrmManager.getRepository<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const calendarChannels = await calendarChannelRepository.find({
select: ['id', 'connectedAccountId'],
where: { id: Any(calendarChannelIds) },
});
const connectedAccountIds = calendarChannels.map(
(calendarChannel) => calendarChannel.connectedAccountId,
);
await connectedAccountRepository.update(
{ id: Any(connectedAccountIds) },
{
authFailedAt: new Date(),
},
);
await this.addToAccountsToReconnect(
calendarChannels.map((calendarChannel) => calendarChannel.id),
workspaceId,
);
},
);
await this.addToAccountsToReconnect(
calendarChannels.map((calendarChannel) => calendarChannel.id),
workspaceId,
);
await this.metricsService.batchIncrementCounter({
key: MetricsKeys.CalendarEventSyncJobFailedInsufficientPermissions,
eventIds: calendarChannelIds,
@@ -291,7 +356,7 @@ export class CalendarChannelSyncStatusService {
}
const calendarChannelRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
await this.globalWorkspaceOrmManager.getRepository<CalendarChannelWorkspaceEntity>(
workspaceId,
'calendarChannel',
);