Get ready for 1000+ members (#14313)
The goal of this PR is to test if Twenty can support a large number of members, a question which was raised by a large company that is considering moving away from Salesforce. I was expecting the currentWorkspaceMembersState to cause a lot more issue. It would be very hard to get rid of it in the context of actors, I think that would require a big refactoring. I thought we'd have to do it but it turns out the perf are pretty good. One thing we need to improve is the pagination on the roles page, we'll wait for @Bonapara to update that
This commit is contained in:
+40
-31
@@ -1,9 +1,12 @@
|
||||
import { CALENDAR_EVENT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
|
||||
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
|
||||
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import {
|
||||
WORKSPACE_MEMBER_DATA_SEED_IDS,
|
||||
getWorkspaceMemberDataSeeds,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
|
||||
type CalendarEventParticipantDataSeed = {
|
||||
export type CalendarEventParticipantDataSeed = {
|
||||
id: string;
|
||||
calendarEventId: string;
|
||||
handle: string;
|
||||
@@ -268,37 +271,43 @@ const CREATE_EVENT_PARTICIPANTS = (
|
||||
return { participants: PARTICIPANTS, nextIndex: participantIndex };
|
||||
};
|
||||
|
||||
const GENERATE_CALENDAR_EVENT_PARTICIPANT_SEEDS =
|
||||
(): CalendarEventParticipantDataSeed[] => {
|
||||
const PARTICIPANT_SEEDS: CalendarEventParticipantDataSeed[] = [];
|
||||
let PARTICIPANT_INDEX = 1;
|
||||
const GENERATE_CALENDAR_EVENT_PARTICIPANT_SEEDS = (
|
||||
workspaceId: string,
|
||||
): CalendarEventParticipantDataSeed[] => {
|
||||
const PARTICIPANT_SEEDS: CalendarEventParticipantDataSeed[] = [];
|
||||
let PARTICIPANT_INDEX = 1;
|
||||
|
||||
const EVENT_IDS = Object.keys(CALENDAR_EVENT_DATA_SEED_IDS).map(
|
||||
(key) =>
|
||||
CALENDAR_EVENT_DATA_SEED_IDS[
|
||||
key as keyof typeof CALENDAR_EVENT_DATA_SEED_IDS
|
||||
],
|
||||
const EVENT_IDS = Object.keys(CALENDAR_EVENT_DATA_SEED_IDS).map(
|
||||
(key) =>
|
||||
CALENDAR_EVENT_DATA_SEED_IDS[
|
||||
key as keyof typeof CALENDAR_EVENT_DATA_SEED_IDS
|
||||
],
|
||||
);
|
||||
|
||||
const PERSON_IDS = Object.keys(PERSON_DATA_SEED_IDS).map(
|
||||
(key) => PERSON_DATA_SEED_IDS[key as keyof typeof PERSON_DATA_SEED_IDS],
|
||||
);
|
||||
const WORKSPACE_MEMBER_IDS = getWorkspaceMemberDataSeeds(workspaceId).map(
|
||||
(member) => member.id,
|
||||
);
|
||||
|
||||
for (const EVENT_ID of EVENT_IDS) {
|
||||
const RESULT = CREATE_EVENT_PARTICIPANTS(
|
||||
EVENT_ID,
|
||||
PERSON_IDS,
|
||||
WORKSPACE_MEMBER_IDS,
|
||||
PARTICIPANT_INDEX,
|
||||
);
|
||||
|
||||
const PERSON_IDS = Object.keys(PERSON_DATA_SEED_IDS).map(
|
||||
(key) => PERSON_DATA_SEED_IDS[key as keyof typeof PERSON_DATA_SEED_IDS],
|
||||
);
|
||||
const WORKSPACE_MEMBER_IDS = Object.values(WORKSPACE_MEMBER_DATA_SEED_IDS);
|
||||
PARTICIPANT_SEEDS.push(...RESULT.participants);
|
||||
PARTICIPANT_INDEX = RESULT.nextIndex;
|
||||
}
|
||||
|
||||
for (const EVENT_ID of EVENT_IDS) {
|
||||
const RESULT = CREATE_EVENT_PARTICIPANTS(
|
||||
EVENT_ID,
|
||||
PERSON_IDS,
|
||||
WORKSPACE_MEMBER_IDS,
|
||||
PARTICIPANT_INDEX,
|
||||
);
|
||||
return PARTICIPANT_SEEDS;
|
||||
};
|
||||
|
||||
PARTICIPANT_SEEDS.push(...RESULT.participants);
|
||||
PARTICIPANT_INDEX = RESULT.nextIndex;
|
||||
}
|
||||
|
||||
return PARTICIPANT_SEEDS;
|
||||
};
|
||||
|
||||
export const CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS =
|
||||
GENERATE_CALENDAR_EVENT_PARTICIPANT_SEEDS();
|
||||
export const getCalendarEventParticipantDataSeeds = (
|
||||
workspaceId: string,
|
||||
): CalendarEventParticipantDataSeed[] => {
|
||||
return GENERATE_CALENDAR_EVENT_PARTICIPANT_SEEDS(workspaceId);
|
||||
};
|
||||
|
||||
+16
-6
@@ -1,8 +1,11 @@
|
||||
import { MESSAGE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
|
||||
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
|
||||
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import {
|
||||
WORKSPACE_MEMBER_DATA_SEED_IDS,
|
||||
getWorkspaceMemberDataSeeds,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
|
||||
type MessageParticipantDataSeed = {
|
||||
export type MessageParticipantDataSeed = {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
@@ -259,7 +262,9 @@ const CREATE_MESSAGE_PARTICIPANTS = (
|
||||
return { participants: PARTICIPANTS, nextIndex: participantIndex };
|
||||
};
|
||||
|
||||
const GENERATE_MESSAGE_PARTICIPANT_SEEDS = (): MessageParticipantDataSeed[] => {
|
||||
const GENERATE_MESSAGE_PARTICIPANT_SEEDS = (
|
||||
workspaceId: string,
|
||||
): MessageParticipantDataSeed[] => {
|
||||
const PARTICIPANT_SEEDS: MessageParticipantDataSeed[] = [];
|
||||
let PARTICIPANT_INDEX = 1;
|
||||
|
||||
@@ -270,7 +275,9 @@ const GENERATE_MESSAGE_PARTICIPANT_SEEDS = (): MessageParticipantDataSeed[] => {
|
||||
const PERSON_IDS = Object.keys(PERSON_DATA_SEED_IDS).map(
|
||||
(key) => PERSON_DATA_SEED_IDS[key as keyof typeof PERSON_DATA_SEED_IDS],
|
||||
);
|
||||
const WORKSPACE_MEMBER_IDS = Object.values(WORKSPACE_MEMBER_DATA_SEED_IDS);
|
||||
const WORKSPACE_MEMBER_IDS = getWorkspaceMemberDataSeeds(workspaceId).map(
|
||||
(member) => member.id,
|
||||
);
|
||||
|
||||
for (const MESSAGE_ID of MESSAGE_IDS) {
|
||||
const RESULT = CREATE_MESSAGE_PARTICIPANTS(
|
||||
@@ -287,5 +294,8 @@ const GENERATE_MESSAGE_PARTICIPANT_SEEDS = (): MessageParticipantDataSeed[] => {
|
||||
return PARTICIPANT_SEEDS;
|
||||
};
|
||||
|
||||
export const MESSAGE_PARTICIPANT_DATA_SEEDS =
|
||||
GENERATE_MESSAGE_PARTICIPANT_SEEDS();
|
||||
export const getMessageParticipantDataSeeds = (
|
||||
workspaceId: string,
|
||||
): MessageParticipantDataSeed[] => {
|
||||
return GENERATE_MESSAGE_PARTICIPANT_SEEDS(workspaceId);
|
||||
};
|
||||
|
||||
+37
-1
@@ -1,4 +1,9 @@
|
||||
import { generateRandomUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-random-users.util';
|
||||
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
|
||||
import {
|
||||
SEED_APPLE_WORKSPACE_ID,
|
||||
SEED_YCOMBINATOR_WORKSPACE_ID,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
|
||||
|
||||
type WorkspaceMemberDataSeed = {
|
||||
id: string;
|
||||
@@ -28,7 +33,14 @@ export const WORKSPACE_MEMBER_DATA_SEED_IDS = {
|
||||
JANE: '20202020-463f-435b-828c-107e007a2711',
|
||||
};
|
||||
|
||||
export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
|
||||
const {
|
||||
workspaceMembers: randomWorkspaceMembers,
|
||||
workspaceMemberIds: randomWorkspaceMemberIds,
|
||||
} = generateRandomUsers();
|
||||
|
||||
export const RANDOM_WORKSPACE_MEMBER_IDS = randomWorkspaceMemberIds;
|
||||
|
||||
const originalWorkspaceMembers: WorkspaceMemberDataSeed[] = [
|
||||
{
|
||||
id: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
|
||||
nameFirstName: 'Tim',
|
||||
@@ -66,3 +78,27 @@ export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
|
||||
userId: USER_DATA_SEED_IDS.JANE,
|
||||
},
|
||||
];
|
||||
|
||||
export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
|
||||
...originalWorkspaceMembers,
|
||||
...randomWorkspaceMembers,
|
||||
];
|
||||
|
||||
export const getWorkspaceMemberDataSeeds = (
|
||||
workspaceId: string,
|
||||
): WorkspaceMemberDataSeed[] => {
|
||||
// In test environment, only return original members to avoid conflicts
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return originalWorkspaceMembers;
|
||||
}
|
||||
|
||||
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
|
||||
// Apple workspace gets all workspace members (original + random)
|
||||
return WORKSPACE_MEMBER_DATA_SEEDS;
|
||||
} else if (workspaceId === SEED_YCOMBINATOR_WORKSPACE_ID) {
|
||||
// YC workspace gets all 4 original workspace members
|
||||
return originalWorkspaceMembers;
|
||||
}
|
||||
|
||||
return originalWorkspaceMembers;
|
||||
};
|
||||
|
||||
+8
-8
@@ -20,7 +20,7 @@ import {
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
|
||||
import {
|
||||
CALENDAR_EVENT_PARTICIPANT_DATA_SEED_COLUMNS,
|
||||
CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS,
|
||||
getCalendarEventParticipantDataSeeds,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-participant-data-seeds.constant';
|
||||
import {
|
||||
COMPANY_DATA_SEED_COLUMNS,
|
||||
@@ -43,8 +43,8 @@ import {
|
||||
MESSAGE_DATA_SEEDS,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
|
||||
import {
|
||||
getMessageParticipantDataSeeds,
|
||||
MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS,
|
||||
MESSAGE_PARTICIPANT_DATA_SEEDS,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant';
|
||||
import {
|
||||
MESSAGE_THREAD_DATA_SEED_COLUMNS,
|
||||
@@ -83,17 +83,17 @@ import {
|
||||
TASK_TARGET_DATA_SEEDS,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/task-target-data-seeds.constant';
|
||||
import {
|
||||
getWorkspaceMemberDataSeeds,
|
||||
WORKSPACE_MEMBER_DATA_SEED_COLUMNS,
|
||||
WORKSPACE_MEMBER_DATA_SEEDS,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import { TimelineActivitySeederService } from 'src/engine/workspace-manager/dev-seeder/data/services/timeline-activity-seeder.service';
|
||||
import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
|
||||
const RECORD_SEEDS_CONFIGS = [
|
||||
const getRecordSeedsConfigs = (workspaceId: string) => [
|
||||
{
|
||||
tableName: 'workspaceMember',
|
||||
pgColumns: WORKSPACE_MEMBER_DATA_SEED_COLUMNS,
|
||||
recordSeeds: WORKSPACE_MEMBER_DATA_SEEDS,
|
||||
recordSeeds: getWorkspaceMemberDataSeeds(workspaceId),
|
||||
},
|
||||
{
|
||||
tableName: 'company',
|
||||
@@ -143,7 +143,7 @@ const RECORD_SEEDS_CONFIGS = [
|
||||
{
|
||||
tableName: 'calendarEventParticipant',
|
||||
pgColumns: CALENDAR_EVENT_PARTICIPANT_DATA_SEED_COLUMNS,
|
||||
recordSeeds: CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS,
|
||||
recordSeeds: getCalendarEventParticipantDataSeeds(workspaceId),
|
||||
},
|
||||
{
|
||||
tableName: 'messageChannel',
|
||||
@@ -168,7 +168,7 @@ const RECORD_SEEDS_CONFIGS = [
|
||||
{
|
||||
tableName: 'messageParticipant',
|
||||
pgColumns: MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS,
|
||||
recordSeeds: MESSAGE_PARTICIPANT_DATA_SEEDS,
|
||||
recordSeeds: getMessageParticipantDataSeeds(workspaceId),
|
||||
},
|
||||
{
|
||||
tableName: '_pet',
|
||||
@@ -213,7 +213,7 @@ export class DevSeederDataService {
|
||||
|
||||
await this.coreDataSource.transaction(
|
||||
async (entityManager: WorkspaceEntityManager) => {
|
||||
for (const recordSeedsConfig of RECORD_SEEDS_CONFIGS) {
|
||||
for (const recordSeedsConfig of getRecordSeedsConfigs(workspaceId)) {
|
||||
const objectMetadata = objectMetadataItems.find(
|
||||
(item) =>
|
||||
computeTableName(item.nameSingular, item.isCustom) ===
|
||||
|
||||
+39
-10
@@ -5,10 +5,16 @@ import chunk from 'lodash.chunk';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { CALENDAR_EVENT_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant';
|
||||
import { CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-participant-data-seeds.constant';
|
||||
import {
|
||||
CalendarEventParticipantDataSeed,
|
||||
getCalendarEventParticipantDataSeeds,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-participant-data-seeds.constant';
|
||||
import { COMPANY_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/company-data-seeds.constant';
|
||||
import { MESSAGE_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
|
||||
import { MESSAGE_PARTICIPANT_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant';
|
||||
import {
|
||||
getMessageParticipantDataSeeds,
|
||||
MessageParticipantDataSeed,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant';
|
||||
import { NOTE_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/note-data-seeds.constant';
|
||||
import { NOTE_TARGET_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/note-target-data-seeds.constant';
|
||||
import { OPPORTUNITY_DATA_SEEDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/opportunity-data-seeds.constant';
|
||||
@@ -116,6 +122,10 @@ export class TimelineActivitySeederService {
|
||||
schemaName: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
// Get workspace-specific participant data
|
||||
const calendarEventParticipants =
|
||||
getCalendarEventParticipantDataSeeds(workspaceId);
|
||||
const messageParticipants = getMessageParticipantDataSeeds(workspaceId);
|
||||
const timelineActivities: TimelineActivitySeedData[] = [];
|
||||
const metadataIds = await this.getObjectMetadataIds(workspaceId);
|
||||
let activityIndex = 0;
|
||||
@@ -159,6 +169,8 @@ export class TimelineActivitySeederService {
|
||||
index,
|
||||
activityIndex,
|
||||
linkedObjectMetadataId,
|
||||
calendarEventParticipants,
|
||||
messageParticipants,
|
||||
});
|
||||
|
||||
timelineActivities.push(...linkedActivities);
|
||||
@@ -179,6 +191,8 @@ export class TimelineActivitySeederService {
|
||||
index,
|
||||
activityIndex,
|
||||
linkedObjectMetadataId,
|
||||
calendarEventParticipants,
|
||||
messageParticipants,
|
||||
});
|
||||
|
||||
timelineActivities.push(...linkedActivities);
|
||||
@@ -367,14 +381,23 @@ export class TimelineActivitySeederService {
|
||||
index,
|
||||
activityIndex,
|
||||
linkedObjectMetadataId,
|
||||
calendarEventParticipants,
|
||||
messageParticipants,
|
||||
}: {
|
||||
activityType: 'note' | 'task' | 'calendarEvent' | 'message';
|
||||
recordSeed: Record<string, unknown>;
|
||||
index: number;
|
||||
activityIndex: number;
|
||||
linkedObjectMetadataId: string;
|
||||
calendarEventParticipants: CalendarEventParticipantDataSeed[];
|
||||
messageParticipants: MessageParticipantDataSeed[];
|
||||
}): TimelineActivitySeedData[] {
|
||||
const targetInfos = this.getActivityTargetInfos(activityType, recordSeed);
|
||||
const targetInfos = this.getActivityTargetInfos(
|
||||
activityType,
|
||||
recordSeed,
|
||||
calendarEventParticipants,
|
||||
messageParticipants,
|
||||
);
|
||||
|
||||
if (targetInfos.length === 0) {
|
||||
return [];
|
||||
@@ -395,12 +418,16 @@ export class TimelineActivitySeederService {
|
||||
private getActivityTargetInfos(
|
||||
activityType: 'note' | 'task' | 'calendarEvent' | 'message',
|
||||
recordSeed: Record<string, unknown>,
|
||||
calendarEventParticipants: CalendarEventParticipantDataSeed[],
|
||||
messageParticipants: MessageParticipantDataSeed[],
|
||||
): ActivityTargetInfo[] {
|
||||
const targetGetters = {
|
||||
note: () => this.getNoteTargetInfos(recordSeed),
|
||||
task: () => this.getTaskTargetInfos(recordSeed),
|
||||
calendarEvent: () => this.getCalendarEventTargetInfos(recordSeed),
|
||||
message: () => this.getMessageTargetInfos(recordSeed),
|
||||
calendarEvent: () =>
|
||||
this.getCalendarEventTargetInfos(recordSeed, calendarEventParticipants),
|
||||
message: () =>
|
||||
this.getMessageTargetInfos(recordSeed, messageParticipants),
|
||||
};
|
||||
|
||||
const getter = targetGetters[activityType];
|
||||
@@ -462,8 +489,9 @@ export class TimelineActivitySeederService {
|
||||
|
||||
private getCalendarEventTargetInfos(
|
||||
recordSeed: Record<string, unknown>,
|
||||
calendarEventParticipants: CalendarEventParticipantDataSeed[],
|
||||
): ActivityTargetInfo[] {
|
||||
const eventParticipants = CALENDAR_EVENT_PARTICIPANT_DATA_SEEDS.filter(
|
||||
const eventParticipants = calendarEventParticipants.filter(
|
||||
(participant) => participant.calendarEventId === recordSeed.id,
|
||||
);
|
||||
|
||||
@@ -473,7 +501,7 @@ export class TimelineActivitySeederService {
|
||||
if (participant.personId) {
|
||||
targetInfos.push({
|
||||
targetType: 'person',
|
||||
targetId: participant.personId as string,
|
||||
targetId: participant.personId,
|
||||
});
|
||||
|
||||
const person = PERSON_DATA_SEEDS.find(
|
||||
@@ -494,14 +522,15 @@ export class TimelineActivitySeederService {
|
||||
|
||||
private getMessageTargetInfos(
|
||||
recordSeed: Record<string, unknown>,
|
||||
messageParticipants: MessageParticipantDataSeed[],
|
||||
): ActivityTargetInfo[] {
|
||||
const messageParticipants = MESSAGE_PARTICIPANT_DATA_SEEDS.filter(
|
||||
const filteredMessageParticipants = messageParticipants.filter(
|
||||
(participant) => participant.messageId === recordSeed.id,
|
||||
);
|
||||
|
||||
const targetInfos: ActivityTargetInfo[] = [];
|
||||
|
||||
messageParticipants.forEach((participant) => {
|
||||
filteredMessageParticipants.forEach((participant) => {
|
||||
if (participant.personId) {
|
||||
targetInfos.push({
|
||||
targetType: 'person',
|
||||
@@ -515,7 +544,7 @@ export class TimelineActivitySeederService {
|
||||
if (person?.companyId) {
|
||||
targetInfos.push({
|
||||
targetType: 'company',
|
||||
targetId: person.companyId as string,
|
||||
targetId: person.companyId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user