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:
+33
-25
@@ -5,7 +5,8 @@ import { type ObjectRecordNonDestructiveEvent } from 'src/engine/core-modules/ev
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.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 { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { TimelineActivityService } from 'src/modules/timeline/services/timeline-activity.service';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
@@ -14,7 +15,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
export class UpsertTimelineActivityFromInternalEvent {
|
||||
constructor(
|
||||
private readonly timelineActivityService: TimelineActivityService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
@Process(UpsertTimelineActivityFromInternalEvent.name)
|
||||
@@ -33,33 +34,40 @@ export class UpsertTimelineActivityFromInternalEvent {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceEventBatch.workspaceId,
|
||||
WorkspaceMemberWorkspaceEntity,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceEventBatch.workspaceId);
|
||||
|
||||
const userIds = workspaceEventBatch.events
|
||||
.map((event) => event.userId)
|
||||
.filter(isDefined);
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceEventBatch.workspaceId,
|
||||
WorkspaceMemberWorkspaceEntity,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceMembers = await workspaceMemberRepository.findBy({
|
||||
userId: In(userIds),
|
||||
});
|
||||
const userIds = workspaceEventBatch.events
|
||||
.map((event) => event.userId)
|
||||
.filter(isDefined);
|
||||
|
||||
for (const eventData of workspaceEventBatch.events) {
|
||||
const workspaceMember = workspaceMembers.find(
|
||||
(workspaceMember) => workspaceMember.userId === eventData.userId,
|
||||
);
|
||||
const workspaceMembers = await workspaceMemberRepository.findBy({
|
||||
userId: In(userIds),
|
||||
});
|
||||
|
||||
if (eventData.userId && workspaceMember) {
|
||||
eventData.workspaceMemberId = workspaceMember.id;
|
||||
}
|
||||
}
|
||||
for (const eventData of workspaceEventBatch.events) {
|
||||
const workspaceMember = workspaceMembers.find(
|
||||
(workspaceMember) => workspaceMember.userId === eventData.userId,
|
||||
);
|
||||
|
||||
await this.timelineActivityService.upsertEvents(workspaceEventBatch);
|
||||
if (eventData.userId && workspaceMember) {
|
||||
eventData.workspaceMemberId = workspaceMember.id;
|
||||
}
|
||||
}
|
||||
|
||||
await this.timelineActivityService.upsertEvents(workspaceEventBatch);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+65
-52
@@ -5,7 +5,8 @@ import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { In, MoreThan } from 'typeorm';
|
||||
|
||||
import { objectRecordDiffMerge } from 'src/engine/core-modules/event-emitter/utils/object-record-diff-merge';
|
||||
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 TimelineActivityPayload } from 'src/modules/timeline/types/timeline-activity-payload';
|
||||
|
||||
type TimelineActivityPayloadWorkspaceIdAndObjectSingularName = {
|
||||
@@ -19,7 +20,7 @@ type TimelineActivityPayloadWorkspaceIdAndObjectSingularName = {
|
||||
@Injectable()
|
||||
export class TimelineActivityRepository {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
async upsertTimelineActivities({
|
||||
@@ -27,61 +28,73 @@ export class TimelineActivityRepository {
|
||||
workspaceId,
|
||||
payloads,
|
||||
}: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) {
|
||||
const recentTimelineActivities = await this.findRecentTimelineActivities({
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
});
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const payloadsWithDiff = payloads
|
||||
.filter(({ properties }) => {
|
||||
const isDiffEmpty =
|
||||
properties.diff !== null &&
|
||||
properties.diff &&
|
||||
Object.keys(properties.diff).length === 0;
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const recentTimelineActivities =
|
||||
await this.findRecentTimelineActivities({
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
});
|
||||
|
||||
return !isDiffEmpty;
|
||||
})
|
||||
.map(({ properties, ...rest }) => ({
|
||||
...rest,
|
||||
properties: isDefined(properties.diff) ? { diff: properties.diff } : {},
|
||||
}));
|
||||
const payloadsWithDiff = payloads
|
||||
.filter(({ properties }) => {
|
||||
const isDiffEmpty =
|
||||
properties.diff !== null &&
|
||||
properties.diff &&
|
||||
Object.keys(properties.diff).length === 0;
|
||||
|
||||
const payloadsToInsert: TimelineActivityPayloadWorkspaceIdAndObjectSingularName['payloads'] =
|
||||
[];
|
||||
return !isDiffEmpty;
|
||||
})
|
||||
.map(({ properties, ...rest }) => ({
|
||||
...rest,
|
||||
properties: isDefined(properties.diff)
|
||||
? { diff: properties.diff }
|
||||
: {},
|
||||
}));
|
||||
|
||||
for (const payload of payloadsWithDiff) {
|
||||
const recentTimelineActivity = recentTimelineActivities.find(
|
||||
(timelineActivity) =>
|
||||
timelineActivity[`${objectSingularName}Id`] === payload.recordId &&
|
||||
timelineActivity.workspaceMemberId === payload.workspaceMemberId &&
|
||||
(!isDefined(payload.linkedRecordId) ||
|
||||
timelineActivity.linkedRecordId === payload.linkedRecordId) &&
|
||||
timelineActivity.name === payload.name,
|
||||
);
|
||||
const payloadsToInsert: TimelineActivityPayloadWorkspaceIdAndObjectSingularName['payloads'] =
|
||||
[];
|
||||
|
||||
if (recentTimelineActivity) {
|
||||
const mergedProperties = objectRecordDiffMerge(
|
||||
recentTimelineActivity.properties,
|
||||
payload.properties,
|
||||
);
|
||||
for (const payload of payloadsWithDiff) {
|
||||
const recentTimelineActivity = recentTimelineActivities.find(
|
||||
(timelineActivity) =>
|
||||
timelineActivity[`${objectSingularName}Id`] ===
|
||||
payload.recordId &&
|
||||
timelineActivity.workspaceMemberId ===
|
||||
payload.workspaceMemberId &&
|
||||
(!isDefined(payload.linkedRecordId) ||
|
||||
timelineActivity.linkedRecordId === payload.linkedRecordId) &&
|
||||
timelineActivity.name === payload.name,
|
||||
);
|
||||
|
||||
await this.updateTimelineActivity({
|
||||
id: recentTimelineActivity.id,
|
||||
properties: mergedProperties,
|
||||
workspaceMemberId: payload.workspaceMemberId,
|
||||
if (recentTimelineActivity) {
|
||||
const mergedProperties = objectRecordDiffMerge(
|
||||
recentTimelineActivity.properties,
|
||||
payload.properties,
|
||||
);
|
||||
|
||||
await this.updateTimelineActivity({
|
||||
id: recentTimelineActivity.id,
|
||||
properties: mergedProperties,
|
||||
workspaceMemberId: payload.workspaceMemberId,
|
||||
workspaceId,
|
||||
});
|
||||
} else {
|
||||
payloadsToInsert.push(payload);
|
||||
}
|
||||
}
|
||||
|
||||
await this.insertTimelineActivities({
|
||||
objectSingularName,
|
||||
payloads: payloadsToInsert,
|
||||
workspaceId,
|
||||
});
|
||||
} else {
|
||||
payloadsToInsert.push(payload);
|
||||
}
|
||||
}
|
||||
|
||||
await this.insertTimelineActivities({
|
||||
objectSingularName,
|
||||
payloads: payloadsToInsert,
|
||||
workspaceId,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private async findRecentTimelineActivities({
|
||||
@@ -90,7 +103,7 @@ export class TimelineActivityRepository {
|
||||
payloads,
|
||||
}: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) {
|
||||
const timelineActivityTypeORMRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
'timelineActivity',
|
||||
{
|
||||
@@ -128,7 +141,7 @@ export class TimelineActivityRepository {
|
||||
}
|
||||
|
||||
const timelineActivityTypeORMRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
'timelineActivity',
|
||||
{
|
||||
@@ -161,7 +174,7 @@ export class TimelineActivityRepository {
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const timelineActivityTypeORMRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
'timelineActivity',
|
||||
{
|
||||
|
||||
@@ -8,7 +8,8 @@ import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/work
|
||||
import { type ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
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 { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { parseEventNameOrThrow } from 'src/engine/workspace-event-emitter/utils/parse-event-name';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
@@ -24,7 +25,7 @@ export class TimelineActivityService {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(TimelineActivityWorkspaceEntity)
|
||||
private readonly timelineActivityRepository: TimelineActivityRepository,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
@@ -177,21 +178,29 @@ export class TimelineActivityService {
|
||||
|
||||
const { action } = parseEventNameOrThrow(name);
|
||||
|
||||
const activityTargetRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
this.targetObjects[activityType],
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const activityTargets =
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const activityTargetRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
this.targetObjects[activityType],
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
return activityTargetRepository.find({
|
||||
where: {
|
||||
[`${activityType}Id`]: In(events.map((event) => event.recordId)),
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const activityTargets = await activityTargetRepository.find({
|
||||
where: {
|
||||
[`${activityType}Id`]: In(events.map((event) => event.recordId)),
|
||||
},
|
||||
});
|
||||
|
||||
if (activityTargets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -254,30 +263,38 @@ export class TimelineActivityService {
|
||||
}): Promise<TimelineActivityPayload[]> {
|
||||
const { action } = parseEventNameOrThrow(name);
|
||||
|
||||
const activityRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
activityType,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const activities =
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const activityRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
activityType,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
return activityRepository.find({
|
||||
where: {
|
||||
id: In(
|
||||
events
|
||||
.map((event) =>
|
||||
this.extractActivityIdFromActivityTargetEvent(
|
||||
event,
|
||||
activityType,
|
||||
),
|
||||
)
|
||||
.filter(isDefined),
|
||||
),
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const activities = await activityRepository.find({
|
||||
where: {
|
||||
id: In(
|
||||
events
|
||||
.map((event) =>
|
||||
this.extractActivityIdFromActivityTargetEvent(
|
||||
event,
|
||||
activityType,
|
||||
),
|
||||
)
|
||||
.filter(isDefined),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
if (activities.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user