Files
twenty/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts
Marie 463dee3fe6 Remove usages of connectToDataSource and use workspaceDataSource (#11873)
In this PR we are

1. cleaning typeORM service by removing connectToDataSource method
2. using workspaceDataSource instead of mainDataSource when possible,
and replacing raw SQL with workspaceRepository methods to use
2025-05-07 10:42:51 +02:00

290 lines
11 KiB
TypeScript

import { Logger } from '@nestjs/common';
import { Command, CommandRunner } from 'nest-commander';
import { DataSource } from 'typeorm';
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/database/typeorm-seeds/core/workspaces';
import {
getDevSeedCompanyCustomFields,
getDevSeedPeopleCustomFields,
} from 'src/database/typeorm-seeds/metadata/fieldsMetadata';
import { seedApiKey } from 'src/database/typeorm-seeds/workspace/api-key';
import { seedCalendarChannels } from 'src/database/typeorm-seeds/workspace/calendar-channel';
import { seedCalendarChannelEventAssociations } from 'src/database/typeorm-seeds/workspace/calendar-channel-event-association';
import { seedCalendarEventParticipants } from 'src/database/typeorm-seeds/workspace/calendar-event-participants';
import { seedCalendarEvents } from 'src/database/typeorm-seeds/workspace/calendar-events';
import { seedCompanies } from 'src/database/typeorm-seeds/workspace/companies';
import { seedConnectedAccount } from 'src/database/typeorm-seeds/workspace/connected-account';
import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites';
import { seedMessageChannelMessageAssociation } from 'src/database/typeorm-seeds/workspace/message-channel-message-associations';
import { seedMessageChannel } from 'src/database/typeorm-seeds/workspace/message-channels';
import { seedMessageParticipant } from 'src/database/typeorm-seeds/workspace/message-participants';
import { seedMessageThread } from 'src/database/typeorm-seeds/workspace/message-threads';
import { seedMessage } from 'src/database/typeorm-seeds/workspace/messages';
import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunities';
import { seedPeople } from 'src/database/typeorm-seeds/workspace/seedPeople';
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { rawDataSource } from 'src/database/typeorm/raw/raw.datasource';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { PETS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/pets-data-seeds';
import { SURVEY_RESULTS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/survey-results-data-seeds';
import { PETS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/pets-metadata-seeds';
import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds';
import { SeederService } from 'src/engine/seeder/seeder.service';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views';
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
import { opportunitiesTableByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-table-by-stage.view';
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
// TODO: implement dry-run
@Command({
name: 'workspace:seed:dev',
description:
'Seed workspace with initial data. This command is intended for development only.',
})
export class DataSeedWorkspaceCommand extends CommandRunner {
workspaceIds = [SEED_APPLE_WORKSPACE_ID, SEED_ACME_WORKSPACE_ID];
private readonly logger = new Logger(DataSeedWorkspaceCommand.name);
constructor(
private readonly dataSourceService: DataSourceService,
private readonly typeORMService: TypeORMService,
private readonly fieldMetadataService: FieldMetadataService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly seederService: SeederService,
private readonly workspaceManagerService: WorkspaceManagerService,
private readonly twentyConfigService: TwentyConfigService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
) {
super();
}
async run(): Promise<void> {
try {
for (const workspaceId of this.workspaceIds) {
await this.createWorkspaceSchema(workspaceId);
}
} catch (error) {
this.logger.error(error);
return;
}
for (const workspaceId of this.workspaceIds) {
await this.seedWorkspace(workspaceId);
}
}
async createWorkspaceSchema(workspaceId: string) {
const workspaceCachedMetadataVersion =
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
await this.workspaceCacheStorageService.flush(
workspaceId,
workspaceCachedMetadataVersion,
);
await rawDataSource.initialize();
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
const appVersion = this.twentyConfigService.get('APP_VERSION');
await seedCoreSchema({
dataSource: rawDataSource,
workspaceId,
seedBilling: isBillingEnabled,
appVersion,
});
await rawDataSource.destroy();
await this.workspaceManagerService.initDev(workspaceId);
}
async seedWorkspace(workspaceId: string) {
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId,
);
const mainDataSource = this.typeORMService.getMainDataSource();
if (!mainDataSource) {
throw new Error('Could not connect to workspace data source');
}
try {
const { objectMetadataStandardIdToIdMap } =
await this.objectMetadataService.getObjectMetadataStandardIdToIdMap(
workspaceId,
);
await this.seedCompanyCustomFields(
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].id,
workspaceId,
);
await this.seedPeopleCustomFields(
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].id,
workspaceId,
);
await this.seedStandardObjectRecords(mainDataSource, dataSourceMetadata);
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
workspaceId,
PETS_METADATA_SEEDS,
PETS_DATA_SEEDS,
);
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
workspaceId,
SURVEY_RESULTS_METADATA_SEEDS,
SURVEY_RESULTS_DATA_SEEDS,
);
} catch (error) {
this.logger.error(error);
}
}
async seedStandardObjectRecords(
mainDataSource: DataSource,
dataSourceMetadata: DataSourceEntity,
) {
await mainDataSource.transaction(
async (entityManager: WorkspaceEntityManager) => {
const { objectMetadataStandardIdToIdMap } =
await this.objectMetadataService.getObjectMetadataStandardIdToIdMap(
dataSourceMetadata.workspaceId,
);
await seedCompanies(entityManager, dataSourceMetadata.schema);
await seedPeople(entityManager, dataSourceMetadata.schema);
await seedOpportunity(entityManager, dataSourceMetadata.schema);
await seedWorkspaceMember(
entityManager,
dataSourceMetadata.schema,
dataSourceMetadata.workspaceId,
);
if (dataSourceMetadata.workspaceId === SEED_APPLE_WORKSPACE_ID) {
await seedApiKey(entityManager, dataSourceMetadata.schema);
await seedMessageThread(entityManager, dataSourceMetadata.schema);
await seedConnectedAccount(entityManager, dataSourceMetadata.schema);
await seedMessage(entityManager, dataSourceMetadata.schema);
await seedMessageChannel(entityManager, dataSourceMetadata.schema);
await seedMessageChannelMessageAssociation(
entityManager,
dataSourceMetadata.schema,
);
await seedMessageParticipant(
entityManager,
dataSourceMetadata.schema,
);
await seedCalendarEvents(entityManager, dataSourceMetadata.schema);
await seedCalendarChannels(entityManager, dataSourceMetadata.schema);
await seedCalendarChannelEventAssociations(
entityManager,
dataSourceMetadata.schema,
);
await seedCalendarEventParticipants(
entityManager,
dataSourceMetadata.schema,
);
}
const viewDefinitionsWithId = await seedViewWithDemoData(
entityManager,
dataSourceMetadata.schema,
objectMetadataStandardIdToIdMap,
);
const devViewDefinitionsWithId = await createWorkspaceViews(
entityManager,
dataSourceMetadata.schema,
[opportunitiesTableByStageView(objectMetadataStandardIdToIdMap)],
);
viewDefinitionsWithId.push(...devViewDefinitionsWithId);
await seedWorkspaceFavorites(
viewDefinitionsWithId
.filter(
(view) =>
view.key === 'INDEX' &&
shouldSeedWorkspaceFavorite(
view.objectMetadataId,
objectMetadataStandardIdToIdMap,
),
)
.map((view) => view.id),
entityManager,
dataSourceMetadata.schema,
);
},
);
}
async seedCompanyCustomFields(
companyObjectMetadataId: string,
workspaceId: string,
) {
if (!companyObjectMetadataId) {
throw new Error(
`Company object metadata not found for workspace ${workspaceId}, can't seed custom fields`,
);
}
const DEV_SEED_COMPANY_CUSTOM_FIELDS = getDevSeedCompanyCustomFields(
companyObjectMetadataId,
workspaceId,
);
await this.fieldMetadataService.createMany(
DEV_SEED_COMPANY_CUSTOM_FIELDS.map((customField) => ({
...customField,
isCustom: true,
})),
);
}
async seedPeopleCustomFields(
personObjectMetadataId: string,
workspaceId: string,
) {
if (!personObjectMetadataId) {
throw new Error(
`Person object metadata not found for workspace ${workspaceId}, can't seed custom fields`,
);
}
const DEV_SEED_PERSON_CUSTOM_FIELDS = getDevSeedPeopleCustomFields(
personObjectMetadataId,
workspaceId,
);
await this.fieldMetadataService.createMany(
DEV_SEED_PERSON_CUSTOM_FIELDS.map((customField) => ({
...customField,
isCustom: true,
})),
);
}
}