diff --git a/packages/twenty-server/src/database/commands/active-workspaces.command.ts b/packages/twenty-server/src/database/commands/active-workspaces.command.ts index 21192dc14..8a0f3e41f 100644 --- a/packages/twenty-server/src/database/commands/active-workspaces.command.ts +++ b/packages/twenty-server/src/database/commands/active-workspaces.command.ts @@ -8,6 +8,8 @@ import { BaseCommandRunner, } from 'src/database/commands/base.command'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; export type ActiveWorkspacesCommandOptions = BaseCommandOptions & { workspaceId?: string; startFromWorkspaceId?: string; @@ -19,7 +21,10 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner { private startFromWorkspaceId: string | undefined; private workspaceCountLimit: number | undefined; - constructor(protected readonly workspaceRepository: Repository) { + constructor( + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { super(); } @@ -122,6 +127,54 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner { ); } + protected async processEachWorkspaceWithWorkspaceDataSource( + workspaceIds: string[], + callback: ({ + workspaceId, + index, + total, + dataSource, + }: { + workspaceId: string; + index: number; + total: number; + dataSource: WorkspaceDataSource; + }) => Promise, + ): Promise { + this.logger.log( + chalk.green(`Running command on ${workspaceIds.length} workspaces`), + ); + for (const [index, workspaceId] of workspaceIds.entries()) { + this.logger.log( + chalk.green( + `Processing workspace ${workspaceId} (${index + 1}/${ + workspaceIds.length + })`, + ), + ); + + const dataSource = + await this.twentyORMGlobalManager.getDataSourceForWorkspace( + workspaceId, + false, + ); + + try { + await callback({ + workspaceId, + index, + total: workspaceIds.length, + dataSource, + }); + } catch (error) { + this.logger.error(`Error in workspace ${workspaceId}: ${error}`); + } + await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( + workspaceId, + ); + } + } + protected abstract executeActiveWorkspacesCommand( passedParams: string[], options: BaseCommandOptions, diff --git a/packages/twenty-server/src/database/commands/database-command.module.ts b/packages/twenty-server/src/database/commands/database-command.module.ts index 23c751bac..402895f95 100644 --- a/packages/twenty-server/src/database/commands/database-command.module.ts +++ b/packages/twenty-server/src/database/commands/database-command.module.ts @@ -7,8 +7,6 @@ import { DataSeedDemoWorkspaceCommand } from 'src/database/commands/data-seed-de import { DataSeedDemoWorkspaceModule } from 'src/database/commands/data-seed-demo-workspace/data-seed-demo-workspace.module'; import { DataSeedWorkspaceCommand } from 'src/database/commands/data-seed-dev-workspace.command'; import { ConfirmationQuestion } from 'src/database/commands/questions/confirmation.question'; -import { UpgradeTo0_40CommandModule } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module'; -import { UpgradeTo0_41CommandModule } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module'; import { UpgradeTo0_42CommandModule } from 'src/database/commands/upgrade-version/0-42/0-42-upgrade-version.module'; import { UpgradeTo0_43CommandModule } from 'src/database/commands/upgrade-version/0-43/0-43-upgrade-version.module'; import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; @@ -51,8 +49,6 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp DataSeedDemoWorkspaceModule, WorkspaceCacheStorageModule, WorkspaceMetadataVersionModule, - UpgradeTo0_40CommandModule, - UpgradeTo0_41CommandModule, UpgradeTo0_42CommandModule, UpgradeTo0_43CommandModule, FeatureFlagModule, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-migrate-aggregate-operations-options.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-migrate-aggregate-operations-options.command.ts deleted file mode 100644 index 279393c12..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-migrate-aggregate-operations-options.command.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import chalk from 'chalk'; -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; -import { v4 } from 'uuid'; -import { isDefined } from 'twenty-shared'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { isCommandLogger } from 'src/database/commands/logger'; -import { AGGREGATE_OPERATIONS } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; -import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; -import { - WorkspaceMigrationColumnActionType, - WorkspaceMigrationTableAction, - WorkspaceMigrationTableActionType, -} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; -import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory'; -import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; -import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; -import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; -import { - VIEW_FIELD_STANDARD_FIELD_IDS, - VIEW_STANDARD_FIELD_IDS, -} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; -import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; - -@Command({ - name: 'upgrade-0.40:migrate-aggregate-operation-options', - description: 'Add aggregate operations options to relevant fields', -}) -export class MigrateAggregateOperationOptionsCommand extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - @InjectRepository(FieldMetadataEntity, 'metadata') - private readonly fieldMetadataRepository: Repository, - @InjectRepository(ObjectMetadataEntity, 'metadata') - private readonly objectMetadataRepository: Repository, - private readonly workspaceMigrationService: WorkspaceMigrationService, - private readonly workspaceMigrationFactory: WorkspaceMigrationFactory, - private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, - private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, - ) { - super(workspaceRepository); - } - - ADDITIONAL_AGGREGATE_OPERATIONS = [ - { - value: AGGREGATE_OPERATIONS.countEmpty, - label: 'Count empty', - position: 5, - color: 'red', - }, - { - value: AGGREGATE_OPERATIONS.countNotEmpty, - label: 'Count not empty', - position: 6, - color: 'purple', - }, - { - value: AGGREGATE_OPERATIONS.countUniqueValues, - label: 'Count unique values', - position: 7, - color: 'sky', - }, - { - value: AGGREGATE_OPERATIONS.percentageEmpty, - label: 'Percent empty', - position: 8, - color: 'turquoise', - }, - { - value: AGGREGATE_OPERATIONS.percentageNotEmpty, - label: 'Percent not empty', - position: 9, - color: 'yellow', - }, - ]; - - ADDITIONAL_AGGREGATE_OPERATIONS_VALUES = - this.ADDITIONAL_AGGREGATE_OPERATIONS.map((option) => option.value); - - async executeActiveWorkspacesCommand( - _passedParam: string[], - options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log( - 'Running command to migrate aggregate operations options to include count operations', - ); - - if (isCommandLogger(this.logger)) { - this.logger.setVerbose(options.verbose ?? false); - } - - let workspaceIterator = 1; - - for (const workspaceId of workspaceIds) { - this.logger.log( - `Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`, - ); - - try { - const viewFieldObjectMetadata = - await this.objectMetadataRepository.findOne({ - where: { - workspaceId, - standardId: STANDARD_OBJECT_IDS.viewField, - }, - }); - - if (!isDefined(viewFieldObjectMetadata)) { - throw new Error( - `View field object metadata not found for workspace ${workspaceId}`, - ); - } - - const viewFieldAggregateOperationFieldMetadata = - await this.fieldMetadataRepository.findOne({ - where: { - workspaceId, - objectMetadataId: viewFieldObjectMetadata.id, - standardId: VIEW_FIELD_STANDARD_FIELD_IDS.aggregateOperation, - }, - }); - - if (isDefined(viewFieldAggregateOperationFieldMetadata)) { - await this.updateAggregateOperationField( - workspaceId, - viewFieldAggregateOperationFieldMetadata, - viewFieldObjectMetadata, - ); - } - - const viewObjectMetadata = await this.objectMetadataRepository.findOne({ - where: { - workspaceId, - standardId: STANDARD_OBJECT_IDS.view, - }, - }); - - if (!isDefined(viewObjectMetadata)) { - throw new Error( - `View object metadata not found for workspace ${workspaceId}`, - ); - } - - const viewAggregateOperationFieldMetadata = - await this.fieldMetadataRepository.findOne({ - where: { - workspaceId, - objectMetadataId: viewObjectMetadata.id, - standardId: VIEW_STANDARD_FIELD_IDS.kanbanAggregateOperation, - }, - }); - - if (isDefined(viewAggregateOperationFieldMetadata)) { - await this.updateAggregateOperationField( - workspaceId, - viewAggregateOperationFieldMetadata, - viewObjectMetadata, - ); - } - - if ( - isDefined(viewAggregateOperationFieldMetadata) || - isDefined(viewFieldAggregateOperationFieldMetadata) - ) { - await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( - workspaceId, - ); - - await this.workspaceMetadataVersionService.incrementMetadataVersion( - workspaceId, - ); - } - workspaceIterator++; - this.logger.log( - chalk.green(`Command completed for workspace ${workspaceId}.`), - ); - } catch { - this.logger.log(chalk.red(`Error in workspace ${workspaceId}.`)); - workspaceIterator++; - } - } - this.logger.log(chalk.green(`Command completed!`)); - } - - private async updateAggregateOperationField( - workspaceId: string, - fieldMetadata: FieldMetadataEntity, - objectMetadata: ObjectMetadataEntity, - ) { - if ( - fieldMetadata.options.some((option) => { - return this.ADDITIONAL_AGGREGATE_OPERATIONS_VALUES.includes( - option.value as AGGREGATE_OPERATIONS, - ); - }) - ) { - this.logger.log( - `Aggregate operation field metadata ${fieldMetadata.name} already has the required options`, - ); - } else { - const updatedFieldMetadata = { - ...fieldMetadata, - options: [ - ...fieldMetadata.options, - ...this.ADDITIONAL_AGGREGATE_OPERATIONS.map((operation) => ({ - ...operation, - id: v4(), - })), - ], - }; - - await this.fieldMetadataRepository.save(updatedFieldMetadata); - - await this.workspaceMigrationService.createCustomMigration( - generateMigrationName( - `update-${objectMetadata.nameSingular}-aggregate-operation`, - ), - workspaceId, - [ - { - name: computeObjectTargetTable(objectMetadata), - action: WorkspaceMigrationTableActionType.ALTER, - columns: this.workspaceMigrationFactory.createColumnActions( - WorkspaceMigrationColumnActionType.ALTER, - fieldMetadata, - updatedFieldMetadata, - ), - } satisfies WorkspaceMigrationTableAction, - ], - ); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-update-inactive-workspace-status.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-update-inactive-workspace-status.command.ts deleted file mode 100644 index 8d54df961..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-update-inactive-workspace-status.command.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import chalk from 'chalk'; -import { Command, Option } from 'nest-commander'; -import { WorkspaceActivationStatus } from 'twenty-shared'; -import { In, Repository } from 'typeorm'; - -import { - BaseCommandOptions, - BaseCommandRunner, -} from 'src/database/commands/base.command'; -import { rawDataSource } from 'src/database/typeorm/raw/raw.datasource'; -import { TypeORMService } from 'src/database/typeorm/typeorm.service'; -import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum'; -import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity'; -import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; -import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; - -type UpdateInactiveWorkspaceStatusOptions = BaseCommandOptions & { - workspaceIds: string[]; -}; - -@Command({ - name: 'upgrade-0.40:update-inactive-workspace-status', - description: - 'Update the status of inactive workspaces to SUSPENDED and delete them', -}) -export class UpdateInactiveWorkspaceStatusCommand extends BaseCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - @InjectRepository(DataSourceEntity, 'metadata') - protected readonly datasourceRepository: Repository, - @InjectRepository(WorkspaceMigrationEntity, 'metadata') - protected readonly workspaceMigrationRepository: Repository, - @InjectRepository(BillingSubscription, 'core') - protected readonly subscriptionRepository: Repository, - @InjectRepository(FeatureFlag, 'core') - protected readonly featureFlagRepository: Repository, - @InjectRepository(KeyValuePair, 'core') - protected readonly keyValuePairRepository: Repository, - @InjectRepository(UserWorkspace, 'core') - protected readonly userWorkspaceRepository: Repository, - @InjectRepository(User, 'core') - protected readonly userRepository: Repository, - private readonly typeORMService: TypeORMService, - ) { - super(); - } - - @Option({ - flags: '-w, --workspace-ids [workspaceIds]', - description: 'Workspace ids to process (comma separated)', - }) - parseWorkspaceIds(val: string): string[] { - return val.split(','); - } - - override async executeBaseCommand( - _passedParams: string[], - options: UpdateInactiveWorkspaceStatusOptions, - ): Promise { - const whereCondition: any = { - activationStatus: WorkspaceActivationStatus.INACTIVE, - }; - - if (options.workspaceIds?.length > 0) { - whereCondition.id = In(options.workspaceIds); - } - - const workspaces = await this.workspaceRepository.find({ - where: whereCondition, - }); - - if (options.dryRun) { - this.logger.log(chalk.yellow('Dry run mode: No changes will be applied')); - } - - this.logger.log( - chalk.blue( - `Found ${workspaces.length} inactive workspace${ - workspaces.length > 1 ? 's' : '' - }`, - ), - ); - - await rawDataSource.initialize(); - - for (const workspace of workspaces) { - this.logger.log( - chalk.blue( - `Processing workspace ${workspace.id} with name ${workspace.displayName}`, - ), - ); - // Check if the workspace has a datasource - const datasource = await this.datasourceRepository.findOne({ - where: { workspaceId: workspace.id }, - }); - - const schemaName = datasource?.schema; - - const postgresSchemaExists = await this.typeORMService - .getMainDataSource() - .query( - `SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = '${schemaName}'`, - ); - - if (!schemaName || !postgresSchemaExists) { - await this.deleteWorkspaceAndMarkAsSuspended(workspace, options); - continue; - } - - const subscriptions = await this.subscriptionRepository.find({ - where: { workspaceId: workspace.id }, - }); - - if (subscriptions.length > 1) { - this.logger.warn(chalk.red('More than one subscription found')); - continue; - } - - const subscription = subscriptions[0]; - - if (!subscription) { - this.logger.log(chalk.red('No subscription found')); - await this.deleteWorkspaceAndMarkAsSuspendedAndDeleteAllData( - workspace, - schemaName, - options, - ); - continue; - } - - const thirtyDaysAgo = new Date(); - - thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); - - if ( - ([ - SubscriptionStatus.Canceled, - SubscriptionStatus.Incomplete, - SubscriptionStatus.IncompleteExpired, - SubscriptionStatus.Unpaid, - SubscriptionStatus.Paused, - ].includes(subscription.status) && - subscription.canceledAt && - subscription.canceledAt < thirtyDaysAgo) || - (subscription.canceledAt === null && - subscription.updatedAt && - subscription.updatedAt < thirtyDaysAgo) - ) { - await this.deleteWorkspaceAndMarkAsSuspendedAndDeleteAllData( - workspace, - schemaName, - options, - subscription, - ); - continue; - } - - await this.markAsSuspended(workspace, options); - } - } - - private async deleteWorkspaceAndMarkAsSuspended( - workspace: Workspace, - options: UpdateInactiveWorkspaceStatusOptions, - ) { - this.logger.log( - chalk.blue('(!!) Deleting workspace and marking as suspended'), - ); - - if (!options.dryRun) { - await this.workspaceRepository.update(workspace.id, { - activationStatus: WorkspaceActivationStatus.SUSPENDED, - }); - - await this.workspaceRepository.softRemove({ id: workspace.id }); - } - } - - private async deleteWorkspaceAndMarkAsSuspendedAndDeleteAllData( - workspace: Workspace, - schemaName: string, - options: UpdateInactiveWorkspaceStatusOptions, - billingSubscription?: BillingSubscription, - ) { - this.logger.warn( - chalk.blue( - `(!!!) Deleting workspace and marking as suspended and deleting all data for workspace updated at ${workspace.updatedAt} with subscription status ${billingSubscription?.status} and subscription updatedAt ${billingSubscription?.updatedAt} and canceledAt ${billingSubscription?.canceledAt}`, - ), - ); - - if (!options.dryRun) { - await this.workspaceRepository.update(workspace.id, { - activationStatus: WorkspaceActivationStatus.SUSPENDED, - }); - - await this.workspaceRepository.softRemove({ id: workspace.id }); - - await this.datasourceRepository.delete({ workspaceId: workspace.id }); - await this.workspaceMigrationRepository.delete({ - workspaceId: workspace.id, - }); - - await this.featureFlagRepository.delete({ workspaceId: workspace.id }); - await this.keyValuePairRepository.delete({ workspaceId: workspace.id }); - - const userWorkspaces = await this.userWorkspaceRepository.find({ - where: { workspaceId: workspace.id }, - }); - - for (const userWorkspace of userWorkspaces) { - await this.userWorkspaceRepository.delete({ id: userWorkspace.id }); - - const remainingUserWorkspaces = - await this.userWorkspaceRepository.count({ - where: { userId: userWorkspace.userId }, - }); - - if (remainingUserWorkspaces === 0) { - await this.userRepository.softRemove({ id: userWorkspace.userId }); - } - } - - await this.typeORMService - .getMainDataSource() - .query(`DROP SCHEMA IF EXISTS ${schemaName} CASCADE`); - } - } - - private async markAsSuspended( - workspace: Workspace, - options: UpdateInactiveWorkspaceStatusOptions, - ) { - this.logger.log(chalk.blue('(!) Marking as suspended')); - if (!options.dryRun) { - await this.workspaceRepository.update(workspace.id, { - activationStatus: WorkspaceActivationStatus.SUSPENDED, - }); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts deleted file mode 100644 index 11f965a83..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; - -import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; -import { BaseCommandOptions } from 'src/database/commands/base.command'; -import { MigrateAggregateOperationOptionsCommand } from 'src/database/commands/upgrade-version/0-40/0-40-migrate-aggregate-operations-options.command'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; - -@Command({ - name: 'upgrade-0.40', - description: 'Upgrade to 0.40', -}) -export class UpgradeTo0_40Command extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly migrateAggregateOperationOptionsCommand: MigrateAggregateOperationOptionsCommand, - ) { - super(workspaceRepository); - } - - async executeActiveWorkspacesCommand( - passedParam: string[], - options: BaseCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to upgrade to 0.40'); - - await this.migrateAggregateOperationOptionsCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts deleted file mode 100644 index 6ce143d50..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { MigrateAggregateOperationOptionsCommand } from 'src/database/commands/upgrade-version/0-40/0-40-migrate-aggregate-operations-options.command'; -import { UpdateInactiveWorkspaceStatusCommand } from 'src/database/commands/upgrade-version/0-40/0-40-update-inactive-workspace-status.command'; -import { UpgradeTo0_40Command } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command'; -import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; -import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; -import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity'; -import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; -import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; -import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module'; -import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature( - [ - Workspace, - BillingSubscription, - FeatureFlag, - KeyValuePair, - User, - UserWorkspace, - ], - 'core', - ), - TypeOrmModule.forFeature( - [ - ObjectMetadataEntity, - FieldMetadataEntity, - DataSourceEntity, - WorkspaceMigrationEntity, - ], - 'metadata', - ), - WorkspaceMigrationRunnerModule, - WorkspaceMigrationModule, - WorkspaceMetadataVersionModule, - TypeORMModule, - ], - providers: [ - UpgradeTo0_40Command, - MigrateAggregateOperationOptionsCommand, - UpdateInactiveWorkspaceStatusCommand, - ], -}) -export class UpgradeTo0_40CommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-add-context-to-actor-composite-type.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-add-context-to-actor-composite-type.ts deleted file mode 100644 index e7a78fed2..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-add-context-to-actor-composite-type.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import chalk from 'chalk'; -import { Command } from 'nest-commander'; -import { FieldMetadataType } from 'twenty-shared'; -import { In, Repository, TableColumn } from 'typeorm'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { CommandLogger } from 'src/database/commands/logger'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { computeTableName } from 'src/engine/utils/compute-table-name.util'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; - -@Command({ - name: 'upgrade-0.41:add-context-to-actor-composite-type', - description: 'Add context to actor composite type.', -}) -export class AddContextToActorCompositeTypeCommand extends ActiveWorkspacesCommandRunner { - protected readonly logger; - - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - @InjectRepository(FieldMetadataEntity, 'metadata') - private readonly fieldMetadataRepository: Repository, - private readonly workspaceDataSourceService: WorkspaceDataSourceService, - private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, - ) { - super(workspaceRepository); - this.logger = new CommandLogger({ - constructorName: this.constructor.name, - verbose: false, - }); - this.logger.setVerbose(false); - } - - async executeActiveWorkspacesCommand( - _passedParam: string[], - options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log(`Running add-context-to-actor-composite-type command`); - - if (options?.dryRun) { - this.logger.log(chalk.yellow('Dry run mode: No changes will be applied')); - } - - for (const workspaceId of workspaceIds) { - try { - await this.execute(workspaceId, options?.dryRun); - this.logger.verbose(`Added for workspace: ${workspaceId}`); - } catch (error) { - this.logger.error(`Error for workspace: ${workspaceId}`, error); - } - } - } - - private async execute(workspaceId: string, dryRun = false): Promise { - this.logger.verbose(`Adding for workspace: ${workspaceId}`); - const actorFields = await this.fieldMetadataRepository.find({ - where: { - type: FieldMetadataType.ACTOR, - workspaceId, - }, - relations: ['object'], - }); - - // Filter and update fields with EMAIL or CALENDAR source - for (const field of actorFields) { - if (!field || !field.object) { - this.logger.verbose( - 'field.objectMetadata is null', - workspaceId, - field.id, - ); - continue; - } - - await this.addContextColumn( - field, - `${field.name}Context`, - workspaceId, - dryRun, - ); - - const fieldRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - field.object.nameSingular, - ); - - if (!dryRun) { - const rowsToUpdate = await fieldRepository.update( - { - [field.name + 'Source']: In([ - FieldActorSource.EMAIL, - FieldActorSource.CALENDAR, - ]), - [field.name + 'Context']: {}, - }, - { - [field.name + 'Context']: { - provider: 'google', - }, - }, - ); - - this.logger.verbose( - `updated ${rowsToUpdate ? rowsToUpdate.affected : 0} rows`, - ); - } - } - - if (!dryRun) { - await this.workspaceMetadataVersionService.incrementMetadataVersion( - workspaceId, - ); - } - - await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( - workspaceId, - ); - } - - private async addContextColumn( - field: FieldMetadataEntity, - newColumnName: string, - workspaceId: string, - dryRun = false, - ): Promise { - const workspaceDataSource = - await this.workspaceDataSourceService.connectToWorkspaceDataSource( - workspaceId, - ); - - if (!workspaceDataSource) { - this.logger.verbose('No workspace data source found'); - - return; - } - - const queryRunner = workspaceDataSource?.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const schemaName = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - try { - const hasColumn = await queryRunner.hasColumn( - `${schemaName}.${computeTableName( - field.object.nameSingular, - field?.object?.isCustom, - )}`, - newColumnName, - ); - - if (hasColumn) { - return; - } - - if (!dryRun) { - await queryRunner.addColumn( - `${schemaName}.${computeTableName( - field.object.nameSingular, - field?.object?.isCustom, - )}`, - new TableColumn({ - name: newColumnName, - type: 'jsonb', - default: `'{}'::"jsonb"`, - isNullable: true, - }), - ); - - await queryRunner.commitTransaction(); - } - } catch (error) { - await queryRunner.rollbackTransaction(); - - throw error; - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command.ts deleted file mode 100644 index 93f1ba9ed..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import chalk from 'chalk'; -import { Command } from 'nest-commander'; -import { FieldMetadataType } from 'twenty-shared'; -import { Repository } from 'typeorm'; - -import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { isCommandLogger } from 'src/database/commands/logger'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { - deduceRelationDirection, - RelationDirection, -} from 'src/engine/utils/deduce-relation-direction.util'; - -@Command({ - name: 'upgrade-0.41:migrate-relations-to-field-metadata', - description: 'Migrate relations to field metadata', -}) -export class MigrateRelationsToFieldMetadataCommand extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - @InjectRepository(FieldMetadataEntity, 'metadata') - private readonly fieldMetadataRepository: Repository, - ) { - super(workspaceRepository); - } - - async executeActiveWorkspacesCommand( - _passedParam: string[], - options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to create many to one relations'); - - if (isCommandLogger(this.logger)) { - this.logger.setVerbose(options.verbose ?? false); - } - - try { - for (const [index, workspaceId] of workspaceIds.entries()) { - await this.processWorkspace(workspaceId, index, workspaceIds.length); - } - - this.logger.log(chalk.green('Command completed!')); - } catch (error) { - this.logger.log(chalk.red('Error in workspace')); - } - } - - private async processWorkspace( - workspaceId: string, - index: number, - total: number, - ): Promise { - try { - this.logger.log( - `Running command for workspace ${workspaceId} ${index + 1}/${total}`, - ); - - const fieldMetadataCollection = (await this.fieldMetadataRepository.find({ - where: { workspaceId, type: FieldMetadataType.RELATION }, - relations: ['fromRelationMetadata', 'toRelationMetadata'], - })) as unknown as FieldMetadataEntity[]; - - if (!fieldMetadataCollection.length) { - this.logger.log( - chalk.yellow( - `No relation field metadata found for workspace ${workspaceId}.`, - ), - ); - - return; - } - - const fieldMetadataToUpdateCollection = fieldMetadataCollection.map( - (fieldMetadata) => this.mapFieldMetadata(fieldMetadata), - ); - - if (fieldMetadataToUpdateCollection.length > 0) { - await this.fieldMetadataRepository.save( - fieldMetadataToUpdateCollection, - ); - } - - this.logger.log( - chalk.green(`Command completed for workspace ${workspaceId}.`), - ); - } catch { - this.logger.log(chalk.red(`Error in workspace ${workspaceId}.`)); - } - } - - private mapFieldMetadata( - fieldMetadata: FieldMetadataEntity, - ): FieldMetadataEntity { - const relationMetadata = - fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata; - - const relationDirection = deduceRelationDirection( - fieldMetadata, - relationMetadata, - ); - let relationType = relationMetadata.relationType as unknown as RelationType; - - if ( - relationDirection === RelationDirection.TO && - relationType === RelationType.ONE_TO_MANY - ) { - relationType = RelationType.MANY_TO_ONE; - } - - const relationTargetFieldMetadataId = - relationDirection === RelationDirection.FROM - ? relationMetadata.toFieldMetadataId - : relationMetadata.fromFieldMetadataId; - - const relationTargetObjectMetadataId = - relationDirection === RelationDirection.FROM - ? relationMetadata.toObjectMetadataId - : relationMetadata.fromObjectMetadataId; - - return { - ...fieldMetadata, - settings: { - relationType, - onDelete: relationMetadata.onDeleteAction, - }, - relationTargetFieldMetadataId, - relationTargetObjectMetadataId, - }; - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-remove-duplicate-mcmas.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-remove-duplicate-mcmas.ts deleted file mode 100644 index 4d895fa61..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-remove-duplicate-mcmas.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; - -@Command({ - name: 'upgrade-0.41:remove-duplicate-mcmas', - description: 'Remove duplicate mcmas.', -}) -export class RemoveDuplicateMcmasCommand extends ActiveWorkspacesCommandRunner { - protected readonly logger: Logger; - - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - ) { - super(workspaceRepository); - this.logger = new Logger(this.constructor.name); - } - - async executeActiveWorkspacesCommand( - _passedParam: string[], - _options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - const { dryRun } = _options; - - for (const workspaceId of workspaceIds) { - try { - await this.execute(workspaceId, dryRun); - } catch (error) { - this.logger.error( - `Error removing duplicate mcmas for workspace ${workspaceId}: ${error}`, - ); - } - } - } - - private async execute(workspaceId: string, dryRun = false): Promise { - this.logger.log(`Removing duplicate mcmas for workspace: ${workspaceId}`); - - const repository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'messageChannelMessageAssociation', - ); - - const queryBuilder = repository.createQueryBuilder( - 'messageChannelMessageAssociation', - ); - - const duplicateMcmas = await queryBuilder - .select(`"messageChannelId"`) - .addSelect(`"messageId"`) - .where(`"deletedAt" IS NULL`) - .groupBy(`"messageId"`) - .addGroupBy(`"messageChannelId"`) - .having(`COUNT("messageChannelId") > 1`) - .getRawMany(); - - this.logger.log(`Found ${duplicateMcmas.length} duplicate mcmas`); - - for (const duplicateMca of duplicateMcmas) { - const mcmas = await repository.find({ - where: { - messageId: duplicateMca.messageId, - messageChannelId: duplicateMca.messageChannelId, - }, - }); - - this.logger.log( - `Found ${mcmas.length} mcmas for message ${duplicateMca.messageId} and message channel ${duplicateMca.messageChannelId}`, - ); - - const mcaIdsToDelete = mcmas.slice(1).map((mca) => mca.id); - - if (mcaIdsToDelete.length > 0) { - this.logger.log(`Deleting ${mcaIdsToDelete.length} mcas`); - if (!dryRun) { - await repository.delete(mcaIdsToDelete); - } - } - } - - await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( - workspaceId, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command.ts deleted file mode 100644 index a0286d522..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { EntityManager, IsNull, Not, Repository } from 'typeorm'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { TypeORMService } from 'src/database/typeorm/typeorm.service'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views'; -import { workflowRunsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view'; -import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view'; -import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view'; -import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; - -@Command({ - name: 'upgrade-0.41:workflow-seed-views', - description: 'Seed workflow views for workspace.', -}) -export class SeedWorkflowViewsCommand extends ActiveWorkspacesCommandRunner { - protected readonly logger: Logger; - - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - private readonly dataSourceService: DataSourceService, - private readonly typeORMService: TypeORMService, - private readonly objectMetadataService: ObjectMetadataService, - ) { - super(workspaceRepository); - this.logger = new Logger(this.constructor.name); - } - - async executeActiveWorkspacesCommand( - _passedParam: string[], - _options: ActiveWorkspacesCommandOptions, - _workspaceIds: string[], - ): Promise { - const { dryRun } = _options; - - for (const workspaceId of _workspaceIds) { - await this.execute(workspaceId, dryRun); - } - } - - private async execute(workspaceId: string, dryRun = false): Promise { - this.logger.log(`Seeding workflow views for workspace: ${workspaceId}`); - - const workflowObjectMetadata = - await this.objectMetadataService.findOneWithinWorkspace(workspaceId, { - where: { - standardId: STANDARD_OBJECT_IDS.workflow, - }, - }); - - if (!workflowObjectMetadata) { - this.logger.error('Workflow object metadata not found'); - - return; - } - - await this.seedWorkflowViews( - workspaceId, - workflowObjectMetadata.id, - dryRun, - ); - - await this.seedWorkspaceFavorite( - workspaceId, - workflowObjectMetadata.id, - dryRun, - ); - - await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( - workspaceId, - ); - } - - private async seedWorkflowViews( - workspaceId: string, - workflowObjectMetadataId: string, - dryRun = false, - ) { - const viewRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'view', - ); - - const existingWorkflowView = await viewRepository.findOne({ - where: { - objectMetadataId: workflowObjectMetadataId, - }, - }); - - if (existingWorkflowView) { - this.logger.log(`View already exists: ${existingWorkflowView.id}`); - - return; - } - - if (dryRun) { - this.logger.log(`Dry run: not creating view`); - - return; - } - - const { objectMetadataStandardIdToIdMap } = - await this.objectMetadataService.getObjectMetadataStandardIdToIdMap( - workspaceId, - ); - - const dataSourceMetadata = - await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( - workspaceId, - ); - - const workspaceDataSource = - await this.typeORMService.connectToDataSource(dataSourceMetadata); - - if (!workspaceDataSource) { - this.logger.error('Could not connect to workspace data source'); - - return; - } - - const viewDefinitions = [ - workflowsAllView(objectMetadataStandardIdToIdMap), - workflowVersionsAllView(objectMetadataStandardIdToIdMap), - workflowRunsAllView(objectMetadataStandardIdToIdMap), - ]; - - await workspaceDataSource.transaction( - async (entityManager: EntityManager) => { - return createWorkspaceViews( - entityManager, - dataSourceMetadata.schema, - viewDefinitions, - ); - }, - ); - } - - private async seedWorkspaceFavorite( - workspaceId: string, - workflowObjectMetadataId: string, - dryRun = false, - ) { - const viewRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'view', - ); - - const workflowView = await viewRepository.findOne({ - where: { - objectMetadataId: workflowObjectMetadataId, - }, - }); - - if (!workflowView) { - this.logger.error('Workflow view not found'); - - return; - } - - if (dryRun) { - this.logger.log(`Dry run: not creating favorite`); - - return; - } - - const favoriteRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'favorite', - ); - - const existingFavorites = await favoriteRepository.find({ - where: { - viewId: Not(IsNull()), - }, - }); - - const workflowFavorite = existingFavorites.find( - (favorite) => favorite.viewId === workflowView.id, - ); - - if (workflowFavorite) { - this.logger.log(`Favorite already exists: ${workflowFavorite.id}`); - - return; - } - - await favoriteRepository.insert({ - viewId: workflowView.id, - position: existingFavorites.length, - }); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts deleted file mode 100644 index d17356aac..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; - -import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; -import { BaseCommandOptions } from 'src/database/commands/base.command'; -import { AddContextToActorCompositeTypeCommand } from 'src/database/commands/upgrade-version/0-41/0-41-add-context-to-actor-composite-type'; -import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command'; -import { RemoveDuplicateMcmasCommand } from 'src/database/commands/upgrade-version/0-41/0-41-remove-duplicate-mcmas'; -import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; - -@Command({ - name: 'upgrade-0.41', - description: 'Upgrade to 0.41', -}) -export class UpgradeTo0_41Command extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly seedWorkflowViewsCommand: SeedWorkflowViewsCommand, - private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand, - private readonly migrateRelationsToFieldMetadata: MigrateRelationsToFieldMetadataCommand, - private readonly addContextToActorCompositeType: AddContextToActorCompositeTypeCommand, - private readonly removeDuplicateMcmasCommand: RemoveDuplicateMcmasCommand, - ) { - super(workspaceRepository); - } - - async executeActiveWorkspacesCommand( - passedParam: string[], - options: BaseCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to upgrade to 0.41'); - - await this.removeDuplicateMcmasCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.addContextToActorCompositeType.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.syncWorkspaceMetadataCommand.executeActiveWorkspacesCommand( - passedParam, - { - ...options, - force: true, - }, - workspaceIds, - ); - - await this.seedWorkflowViewsCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.migrateRelationsToFieldMetadata.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts deleted file mode 100644 index 946e79537..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { AddContextToActorCompositeTypeCommand } from 'src/database/commands/upgrade-version/0-41/0-41-add-context-to-actor-composite-type'; -import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command'; -import { RemoveDuplicateMcmasCommand } from 'src/database/commands/upgrade-version/0-41/0-41-remove-duplicate-mcmas'; -import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command'; -import { UpgradeTo0_41Command } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command'; -import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; -import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; -import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module'; -import { SyncWorkspaceLoggerService } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.service'; -import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; -import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module'; -import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([Workspace], 'core'), - TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'), - TypeORMModule, - DataSourceModule, - ObjectMetadataModule, - WorkspaceSyncMetadataCommandsModule, - WorkspaceSyncMetadataModule, - WorkspaceHealthModule, - WorkspaceDataSourceModule, - WorkspaceMetadataVersionModule, - ], - providers: [ - SyncWorkspaceLoggerService, - SyncWorkspaceMetadataCommand, - SeedWorkflowViewsCommand, - UpgradeTo0_41Command, - MigrateRelationsToFieldMetadataCommand, - AddContextToActorCompositeTypeCommand, - RemoveDuplicateMcmasCommand, - ], -}) -export class UpgradeTo0_41CommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command.ts index e634cc7f1..c40371740 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command.ts @@ -26,10 +26,10 @@ export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesCommandRu protected readonly workspaceRepository: Repository, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field.ts index 39bc9e30b..29579c153 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field.ts @@ -25,9 +25,9 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); this.logger = new CommandLogger({ constructorName: this.constructor.name, verbose: false, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command.ts index e98f5d527..f3e9352c8 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command.ts @@ -64,6 +64,7 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, @InjectRepository(FieldMetadataEntity, 'metadata') private readonly fieldMetadataRepository: Repository, @InjectRepository(ObjectMetadataEntity, 'metadata') @@ -71,12 +72,11 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { @InjectRepository(FeatureFlag, 'core') protected readonly featureFlagRepository: Repository, private readonly workspaceDataSourceService: WorkspaceDataSourceService, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workspaceMigrationService: WorkspaceMigrationService, private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } @Option({ diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type.ts index 701850440..fa943474d 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type.ts @@ -25,12 +25,13 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, @InjectRepository(FieldMetadataEntity, 'metadata') private readonly fieldMetadataRepository: Repository, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); + this.logger = new CommandLogger({ constructorName: this.constructor.name, verbose: false, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts index 7e2136644..21d6ada90 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts @@ -10,6 +10,7 @@ import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-ver import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-migrate-rich-text-field.command'; import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; type Upgrade042CommandCustomOptions = { @@ -25,13 +26,14 @@ export class UpgradeTo0_42Command extends ActiveWorkspacesCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly migrateRichTextFieldCommand: MigrateRichTextFieldCommand, private readonly fixBodyV2ViewFieldPositionCommand: FixBodyV2ViewFieldPositionCommand, private readonly limitAmountOfViewFieldCommand: LimitAmountOfViewFieldCommand, private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand, private readonly standardizationOfActorCompositeContextType: StandardizationOfActorCompositeContextTypeCommand, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } @Option({ diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command.ts index 790da0990..cf2025357 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command.ts @@ -17,7 +17,6 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { tasksAssignedToMeView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/tasks-assigned-to-me'; -import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; import { TASK_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity'; @@ -37,11 +36,10 @@ export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesCommandRunn private readonly objectMetadataRepository: Repository, @InjectRepository(FieldMetadataEntity, 'metadata') private readonly fieldMetadataRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command.ts index db115bcd4..5ffc4a476 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command.ts @@ -14,6 +14,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { SearchService } from 'src/engine/metadata-modules/search/search.service'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; import { SEARCH_FIELDS_FOR_NOTES } from 'src/modules/note/standard-objects/note.workspace-entity'; import { SEARCH_FIELDS_FOR_TASKS } from 'src/modules/task/standard-objects/task.workspace-entity'; @@ -26,6 +27,7 @@ export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorks constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, @InjectRepository(FeatureFlag, 'core') protected readonly featureFlagRepository: Repository, @InjectRepository(ObjectMetadataEntity, 'metadata') @@ -34,7 +36,7 @@ export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorks private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command.ts index c3e0cf49d..dbe6f3a9a 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command.ts @@ -29,9 +29,9 @@ export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends Acti protected readonly objectMetadataRepository: Repository, private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( @@ -43,9 +43,12 @@ export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends Acti 'Running command to update default view record opening on workflow objects to record page', ); - for (const [index, workspaceId] of workspaceIds.entries()) { - await this.processWorkspace(workspaceId, index, workspaceIds.length); - } + this.processEachWorkspaceWithWorkspaceDataSource( + workspaceIds, + async ({ workspaceId, index, total }) => { + await this.processWorkspace(workspaceId, index, total); + }, + ); this.logger.log(chalk.green('Command completed!')); } @@ -87,14 +90,6 @@ export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends Acti workspaceId, ); - await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( - workspaceId, - ); - - await this.workspaceMetadataVersionService.incrementMetadataVersion( - workspaceId, - ); - this.logger.log( chalk.green(`Command completed for workspace ${workspaceId}.`), ); diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts index a9e99ce6e..e625af194 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts @@ -10,6 +10,7 @@ import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-v import { MigrateSearchVectorOnNoteAndTaskEntitiesCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-search-vector-on-note-and-task-entities.command'; import { UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand } from 'src/database/commands/upgrade-version/0-43/0-43-update-default-view-record-opening-on-workflow-objects.command'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @Command({ name: 'upgrade-0.43', @@ -19,12 +20,13 @@ export class UpgradeTo0_43Command extends ActiveWorkspacesCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly addTasksAssignedToMeViewCommand: AddTasksAssignedToMeViewCommand, private readonly migrateSearchVectorOnNoteAndTaskEntitiesCommand: MigrateSearchVectorOnNoteAndTaskEntitiesCommand, private readonly updateDefaultViewRecordOpeningOnWorkflowObjectsCommand: UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand, private readonly standardizationOfActorCompositeContextTypeCommand: StandardizationOfActorCompositeContextTypeCommand, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( diff --git a/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-customer-data.command.ts b/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-customer-data.command.ts index ab78c62ab..ba2bdc334 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-customer-data.command.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-customer-data.command.ts @@ -13,6 +13,7 @@ import { import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { StripeSubscriptionService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; interface SyncCustomerDataCommandOptions extends ActiveWorkspacesCommandOptions {} @@ -28,8 +29,9 @@ export class BillingSyncCustomerDataCommand extends ActiveWorkspacesCommandRunne private readonly stripeSubscriptionService: StripeSubscriptionService, @InjectRepository(BillingCustomer, 'core') protected readonly billingCustomerRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand( diff --git a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts index e9a2e226c..3fdb6100f 100644 --- a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts +++ b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts @@ -14,13 +14,17 @@ import { } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception'; import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory'; import { CacheManager } from 'src/engine/twenty-orm/storage/cache-manager.storage'; +import { CacheKey } from 'src/engine/twenty-orm/storage/types/cache-key.type'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @Injectable() export class WorkspaceDatasourceFactory { private readonly logger = new Logger(WorkspaceDatasourceFactory.name); private cacheManager = new CacheManager(); - private cachedDatasourcePromise: Record>; + private cachedDataSourcePromise: Record< + CacheKey, + Promise + >; constructor( private readonly dataSourceService: DataSourceService, @@ -29,7 +33,7 @@ export class WorkspaceDatasourceFactory { private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService, private readonly entitySchemaFactory: EntitySchemaFactory, ) { - this.cachedDatasourcePromise = {}; + this.cachedDataSourcePromise = {}; } public async create( @@ -53,16 +57,16 @@ export class WorkspaceDatasourceFactory { ); } - const cacheKey = `${workspaceId}-${cachedWorkspaceMetadataVersion}`; + const cacheKey: CacheKey = `${workspaceId}-${cachedWorkspaceMetadataVersion}`; - if (cacheKey in this.cachedDatasourcePromise) { - return this.cachedDatasourcePromise[cacheKey]; + if (cacheKey in this.cachedDataSourcePromise) { + return this.cachedDataSourcePromise[cacheKey]; } const creationPromise = (async (): Promise => { try { const result = await this.cacheManager.execute( - cacheKey as '`${string}-${string}`', + cacheKey, async () => { this.logger.log( `Creating workspace data source for workspace ${workspaceId} and metadata version ${cachedWorkspaceMetadataVersion}`, @@ -178,22 +182,23 @@ export class WorkspaceDatasourceFactory { return result; } finally { - delete this.cachedDatasourcePromise[cacheKey]; + delete this.cachedDataSourcePromise[cacheKey]; } })(); - this.cachedDatasourcePromise[cacheKey] = creationPromise; + this.cachedDataSourcePromise[cacheKey] = creationPromise; return creationPromise; } public async destroy(workspaceId: string): Promise { - const cachedWorkspaceMetadataVersion = - await this.workspaceCacheStorageService.getMetadataVersion(workspaceId); + const cacheKeys = ( + Object.keys(this.cachedDataSourcePromise) as CacheKey[] + ).filter((key) => key.startsWith(`${workspaceId}`)); - await this.cacheManager.clearKey( - `${workspaceId}-${cachedWorkspaceMetadataVersion}`, - ); + for (const cacheKey of cacheKeys) { + await this.cacheManager.clearKey(cacheKey); + } } private async getWorkspaceMetadataVersionFromCache( diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/cache-manager.storage.ts b/packages/twenty-server/src/engine/twenty-orm/storage/cache-manager.storage.ts index 53bc06093..13792d03b 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/cache-manager.storage.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/cache-manager.storage.ts @@ -1,6 +1,6 @@ import { isDefined } from 'twenty-shared'; -type CacheKey = `${string}-${string}`; +import { CacheKey } from 'src/engine/twenty-orm/storage/types/cache-key.type'; type AsyncFactoryCallback = () => Promise; @@ -52,6 +52,9 @@ export class CacheManager { await onDelete?.(cachedValue); this.cache.delete(cacheKey); } + // TODO: remove this once we have debug on prod + // eslint-disable-next-line no-console + console.log('Datasource cache size: ', this.cache.size); } async clear(onDelete?: (value: T) => Promise | void): Promise { diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/types/cache-key.type.ts b/packages/twenty-server/src/engine/twenty-orm/storage/types/cache-key.type.ts new file mode 100644 index 000000000..c218f25ad --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/storage/types/cache-key.type.ts @@ -0,0 +1 @@ +export type CacheKey = `${string}-${string}`; diff --git a/packages/twenty-server/src/engine/twenty-orm/twenty-orm-global.manager.ts b/packages/twenty-server/src/engine/twenty-orm/twenty-orm-global.manager.ts index acfaabb18..00dd38532 100644 --- a/packages/twenty-server/src/engine/twenty-orm/twenty-orm-global.manager.ts +++ b/packages/twenty-server/src/engine/twenty-orm/twenty-orm-global.manager.ts @@ -50,8 +50,15 @@ export class TwentyORMGlobalManager { return repository; } - async getDataSourceForWorkspace(workspaceId: string) { - return await this.workspaceDataSourceFactory.create(workspaceId, null); + async getDataSourceForWorkspace( + workspaceId: string, + failOnMetadataCacheMiss = true, + ) { + return await this.workspaceDataSourceFactory.create( + workspaceId, + null, + failOnMetadataCacheMiss, + ); } async destroyDataSourceForWorkspace(workspaceId: string) { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts index eef8e92b4..c8372d3da 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts @@ -6,6 +6,7 @@ import { Repository } from 'typeorm'; import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service'; import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service'; @@ -30,8 +31,9 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner private readonly workspaceHealthService: WorkspaceHealthService, private readonly dataSourceService: DataSourceService, private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) { - super(workspaceRepository); + super(workspaceRepository, twentyORMGlobalManager); } async executeActiveWorkspacesCommand(