From a1eea40cf7fc782dabf1420f4fc26e006c6b7710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20M?= Date: Tue, 25 Feb 2025 11:24:05 +0100 Subject: [PATCH] feat: populate relation join column (#10212) Fix https://github.com/twentyhq/core-team-issues/issues/241#issue-2793030259 --- ...ve-workspaces-migration-command.runner.ts} | 82 ++------- ...ive-workspaces-migration-command.runner.ts | 68 +++++++ .../create-upgrade-all-command.factory.ts | 40 +++++ .../decorators/migration-command.decorator.ts | 41 +++++ .../interfaces/migration-command.interface.ts | 5 + .../migration-command.constants.ts | 1 + .../migration-command.runner.ts} | 25 +-- .../miration-command.module.ts | 43 +++++ ...fix-body-v2-view-field-position.command.ts | 14 +- .../0-42/0-42-limit-amount-of-view-field.ts | 25 +-- .../0-42-migrate-rich-text-field.command.ts | 30 ++-- ...ization-of-actor-composite-context-type.ts | 28 +-- .../0-42/0-42-upgrade-version.command.ts | 89 --------- .../0-42/0-42-upgrade-version.module.ts | 41 +++-- ...3-add-tasks-assigned-to-me-view.command.ts | 21 +-- ...ate-relations-to-field-metadata.command.ts | 169 ++++++++++++++++++ ...ector-on-note-and-task-entities.command.ts | 19 +- ...ord-opening-on-workflow-objects.command.ts | 39 +--- .../0-43/0-43-upgrade-version.command.ts | 64 ------- .../0-43/0-43-upgrade-version.module.ts | 39 ++-- .../process-nested-relations-v2.helper.ts | 7 +- .../query-result-getters.factory.ts | 8 +- ...xtend-object-type-definition-v2.factory.ts | 5 +- .../services/type-mapper.service.ts | 6 +- .../billing-sync-customer-data.command.ts | 12 +- .../billing-sync-plans-data.command.ts | 20 +-- .../field-metadata/dtos/field-metadata.dto.ts | 6 +- .../field-metadata-validation.service.ts | 2 +- .../field-metadata/field-metadata.entity.ts | 4 +- .../field-metadata-default-value.interface.ts | 19 +- .../field-metadata-options.interface.ts | 18 +- .../field-metadata-settings.interface.ts | 19 +- .../interfaces/field-metadata.interface.ts | 4 +- .../utils/compute-column-name.util.ts | 12 +- .../dtos/create-object.input.ts | 4 +- .../object-metadata-relation.service.ts | 6 +- .../relation-metadata.service.ts | 6 +- .../remote-table-relations.service.ts | 8 +- .../utils/udt-name-mapper.util.ts | 4 +- .../column-action-abstract.factory.ts | 5 +- ...rkspace-column-action-factory.interface.ts | 4 +- .../survey-results-metadata-seeds.ts | 29 ++- .../decorators/workspace-field.decorator.ts | 2 +- .../utils/is-field-metadata-of-type.util.ts | 28 +++ .../utils/is-relation-field-metadata.util.ts | 9 - .../clean-suspended-workspaces.command.ts | 12 +- .../sync-workspace-metadata.command.ts | 24 +-- packages/twenty-shared/src/types/IsExactly.ts | 5 + packages/twenty-shared/src/types/index.ts | 2 + 49 files changed, 677 insertions(+), 496 deletions(-) rename packages/twenty-server/src/database/commands/{active-workspaces.command.ts => migration-command/active-workspaces-migration-command.runner.ts} (64%) create mode 100644 packages/twenty-server/src/database/commands/migration-command/batch-active-workspaces-migration-command.runner.ts create mode 100644 packages/twenty-server/src/database/commands/migration-command/create-upgrade-all-command.factory.ts create mode 100644 packages/twenty-server/src/database/commands/migration-command/decorators/migration-command.decorator.ts create mode 100644 packages/twenty-server/src/database/commands/migration-command/interfaces/migration-command.interface.ts create mode 100644 packages/twenty-server/src/database/commands/migration-command/migration-command.constants.ts rename packages/twenty-server/src/database/commands/{base.command.ts => migration-command/migration-command.runner.ts} (64%) create mode 100644 packages/twenty-server/src/database/commands/migration-command/miration-command.module.ts delete mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command.ts delete mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts create mode 100644 packages/twenty-server/src/engine/utils/is-field-metadata-of-type.util.ts delete mode 100644 packages/twenty-server/src/engine/utils/is-relation-field-metadata.util.ts create mode 100644 packages/twenty-shared/src/types/IsExactly.ts diff --git a/packages/twenty-server/src/database/commands/active-workspaces.command.ts b/packages/twenty-server/src/database/commands/migration-command/active-workspaces-migration-command.runner.ts similarity index 64% rename from packages/twenty-server/src/database/commands/active-workspaces.command.ts rename to packages/twenty-server/src/database/commands/migration-command/active-workspaces-migration-command.runner.ts index 8a0f3e41f..0d368e1b8 100644 --- a/packages/twenty-server/src/database/commands/active-workspaces.command.ts +++ b/packages/twenty-server/src/database/commands/migration-command/active-workspaces-migration-command.runner.ts @@ -4,19 +4,23 @@ import { WorkspaceActivationStatus } from 'twenty-shared'; import { In, MoreThanOrEqual, Repository } from 'typeorm'; import { - BaseCommandOptions, - BaseCommandRunner, -} from 'src/database/commands/base.command'; + MigrationCommandOptions, + MigrationCommandRunner, +} from 'src/database/commands/migration-command/migration-command.runner'; 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; - workspaceCountLimit?: number; -}; -export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner { +export type ActiveWorkspacesMigrationCommandOptions = + MigrationCommandOptions & { + workspaceId?: string; + startFromWorkspaceId?: string; + workspaceCountLimit?: number; + }; + +export abstract class ActiveWorkspacesMigrationCommandRunner< + Options extends + ActiveWorkspacesMigrationCommandOptions = ActiveWorkspacesMigrationCommandOptions, +> extends MigrationCommandRunner { private workspaceIds: string[] = []; private startFromWorkspaceId: string | undefined; private workspaceCountLimit: number | undefined; @@ -105,9 +109,9 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner { } } - override async executeBaseCommand( + override async runMigrationCommand( passedParams: string[], - options: BaseCommandOptions, + options: Options, ): Promise { const activeWorkspaceIds = this.workspaceIds.length > 0 @@ -120,64 +124,16 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner { this.logger.log(chalk.yellow('Dry run mode: No changes will be applied')); } - await this.executeActiveWorkspacesCommand( + await this.runMigrationCommandOnActiveWorkspaces( passedParams, options, activeWorkspaceIds, ); } - 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( + protected abstract runMigrationCommandOnActiveWorkspaces( passedParams: string[], - options: BaseCommandOptions, + options: Options, activeWorkspaceIds: string[], ): Promise; } diff --git a/packages/twenty-server/src/database/commands/migration-command/batch-active-workspaces-migration-command.runner.ts b/packages/twenty-server/src/database/commands/migration-command/batch-active-workspaces-migration-command.runner.ts new file mode 100644 index 000000000..74a375610 --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/batch-active-workspaces-migration-command.runner.ts @@ -0,0 +1,68 @@ +import chalk from 'chalk'; +import { Repository } from 'typeorm'; + +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +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 abstract class BatchActiveWorkspacesMigrationCommandRunner< + Options extends + ActiveWorkspacesMigrationCommandOptions = ActiveWorkspacesMigrationCommandOptions, +> extends ActiveWorkspacesMigrationCommandRunner { + constructor( + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + async runMigrationCommandOnActiveWorkspaces( + _passedParams: string[], + _options: Options, + activeWorkspaceIds: string[], + ): Promise { + this.logger.log( + chalk.green(`Running command on ${activeWorkspaceIds.length} workspaces`), + ); + for (const [index, workspaceId] of activeWorkspaceIds.entries()) { + this.logger.log( + chalk.green( + `Processing workspace ${workspaceId} (${index + 1}/${ + activeWorkspaceIds.length + })`, + ), + ); + + const dataSource = + await this.twentyORMGlobalManager.getDataSourceForWorkspace( + workspaceId, + false, + ); + + try { + await this.runMigrationCommandOnWorkspace( + workspaceId, + index, + activeWorkspaceIds.length, + dataSource, + ); + } catch (error) { + this.logger.error(`Error in workspace ${workspaceId}: ${error}`); + } + await this.twentyORMGlobalManager.destroyDataSourceForWorkspace( + workspaceId, + ); + } + } + + protected abstract runMigrationCommandOnWorkspace( + workspaceId: string, + index: number, + total: number, + dataSource: WorkspaceDataSource, + ): Promise; +} diff --git a/packages/twenty-server/src/database/commands/migration-command/create-upgrade-all-command.factory.ts b/packages/twenty-server/src/database/commands/migration-command/create-upgrade-all-command.factory.ts new file mode 100644 index 000000000..4d95da353 --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/create-upgrade-all-command.factory.ts @@ -0,0 +1,40 @@ +import { Inject } from '@nestjs/common'; + +import { Command } from 'nest-commander'; + +import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface'; + +import { MIGRATION_COMMAND_INJECTION_TOKEN } from 'src/database/commands/migration-command/migration-command.constants'; +import { MigrationCommandRunner } from 'src/database/commands/migration-command/migration-command.runner'; + +export function createUpgradeAllCommand( + version: string, +): new (...args: unknown[]) => MigrationCommandRunner { + @Command({ + name: `upgrade-${version}`, + description: `Upgrade to version ${version}`, + }) + class UpgradeCommand extends MigrationCommandRunner { + constructor( + @Inject(MIGRATION_COMMAND_INJECTION_TOKEN) + private readonly subCommands: MigrationCommandInterface[], + ) { + super(); + } + + async runMigrationCommand( + passedParams: string[], + options: Record, + ): Promise { + this.logger.log(`Running upgrade command for version ${version}`); + + for (const command of this.subCommands) { + await command.runMigrationCommand(passedParams, options); + } + + this.logger.log(`Upgrade ${version} command completed!`); + } + } + + return UpgradeCommand; +} diff --git a/packages/twenty-server/src/database/commands/migration-command/decorators/migration-command.decorator.ts b/packages/twenty-server/src/database/commands/migration-command/decorators/migration-command.decorator.ts new file mode 100644 index 000000000..760d08f29 --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/decorators/migration-command.decorator.ts @@ -0,0 +1,41 @@ +// migration-command.decorator.ts +import { Type } from '@nestjs/common'; + +import { Command, CommandMetadata } from 'nest-commander'; +import 'reflect-metadata'; + +import { MigrationCommandRunner } from 'src/database/commands/migration-command/migration-command.runner'; + +export interface MigrationCommandMetadata extends CommandMetadata { + version: string; +} + +const MIGRATION_COMMANDS = new Map< + string, + Array> +>(); + +export function MigrationCommand( + options: MigrationCommandMetadata, +): >(target: T) => T | void { + return >(target: T): T | void => { + const { version, name, ...commandOptions } = options; + + if (!MIGRATION_COMMANDS.has(version)) { + MIGRATION_COMMANDS.set(version, []); + } + + MIGRATION_COMMANDS.get(version)?.push(target); + + return Command({ + name: `upgrade-${version}:${name}`, + ...commandOptions, + })(target); + }; +} + +export function getMigrationCommandsForVersion( + version: string, +): Array> { + return MIGRATION_COMMANDS.get(version) || []; +} diff --git a/packages/twenty-server/src/database/commands/migration-command/interfaces/migration-command.interface.ts b/packages/twenty-server/src/database/commands/migration-command/interfaces/migration-command.interface.ts new file mode 100644 index 000000000..8b197cda2 --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/interfaces/migration-command.interface.ts @@ -0,0 +1,5 @@ +export interface MigrationCommandInterface< + Options extends Record = Record, +> { + runMigrationCommand(passedParams: string[], options: Options): Promise; +} diff --git a/packages/twenty-server/src/database/commands/migration-command/migration-command.constants.ts b/packages/twenty-server/src/database/commands/migration-command/migration-command.constants.ts new file mode 100644 index 000000000..b83709364 --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/migration-command.constants.ts @@ -0,0 +1 @@ +export const MIGRATION_COMMAND_INJECTION_TOKEN = 'MIGRATION_COMMANDS'; diff --git a/packages/twenty-server/src/database/commands/base.command.ts b/packages/twenty-server/src/database/commands/migration-command/migration-command.runner.ts similarity index 64% rename from packages/twenty-server/src/database/commands/base.command.ts rename to packages/twenty-server/src/database/commands/migration-command/migration-command.runner.ts index 780eb18c1..801a80e59 100644 --- a/packages/twenty-server/src/database/commands/base.command.ts +++ b/packages/twenty-server/src/database/commands/migration-command/migration-command.runner.ts @@ -3,14 +3,22 @@ import { Logger } from '@nestjs/common'; import chalk from 'chalk'; import { CommandRunner, Option } from 'nest-commander'; -import { CommandLogger } from './logger'; -export type BaseCommandOptions = { +import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface'; + +import { CommandLogger } from 'src/database/commands/logger'; + +export type MigrationCommandOptions = { workspaceId?: string; dryRun?: boolean; verbose?: boolean; }; -export abstract class BaseCommandRunner extends CommandRunner { +export abstract class MigrationCommandRunner< + Options extends MigrationCommandOptions = MigrationCommandOptions, + > + extends CommandRunner + implements MigrationCommandInterface +{ protected logger: CommandLogger | Logger; constructor() { super(); @@ -38,10 +46,7 @@ export abstract class BaseCommandRunner extends CommandRunner { return true; } - override async run( - passedParams: string[], - options: BaseCommandOptions, - ): Promise { + override async run(passedParams: string[], options: Options): Promise { if (options.verbose) { this.logger = new CommandLogger({ verbose: true, @@ -50,7 +55,7 @@ export abstract class BaseCommandRunner extends CommandRunner { } try { - await this.executeBaseCommand(passedParams, options); + await this.runMigrationCommand(passedParams, options); } catch (error) { this.logger.error(chalk.red(`Command failed`)); throw error; @@ -59,8 +64,8 @@ export abstract class BaseCommandRunner extends CommandRunner { } } - protected abstract executeBaseCommand( + abstract runMigrationCommand( passedParams: string[], - options: BaseCommandOptions, + options: Options, ): Promise; } diff --git a/packages/twenty-server/src/database/commands/migration-command/miration-command.module.ts b/packages/twenty-server/src/database/commands/migration-command/miration-command.module.ts new file mode 100644 index 000000000..404245d5f --- /dev/null +++ b/packages/twenty-server/src/database/commands/migration-command/miration-command.module.ts @@ -0,0 +1,43 @@ +import { DynamicModule, Module, ModuleMetadata } from '@nestjs/common'; + +import { MigrationCommandInterface } from 'src/database/commands/migration-command/interfaces/migration-command.interface'; + +import { createUpgradeAllCommand } from 'src/database/commands/migration-command/create-upgrade-all-command.factory'; +import { getMigrationCommandsForVersion } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; +import { MIGRATION_COMMAND_INJECTION_TOKEN } from 'src/database/commands/migration-command/migration-command.constants'; + +@Module({}) +export class MigrationCommandModule { + static register( + version: string, + moduleMetadata: ModuleMetadata, + ): DynamicModule { + const commandClasses = getMigrationCommandsForVersion(version); + const upgradeAllCommand = createUpgradeAllCommand(version); + + return { + module: MigrationCommandModule, + imports: moduleMetadata.imports, + providers: [ + ...(moduleMetadata.providers ?? []), + ...commandClasses, + { + provide: MIGRATION_COMMAND_INJECTION_TOKEN, + useFactory: ( + ...instances: MigrationCommandInterface[] + ): MigrationCommandInterface[] => { + return instances; + }, + inject: commandClasses, + }, + upgradeAllCommand, + ], + exports: [ + ...(moduleMetadata.exports ?? []), + MIGRATION_COMMAND_INJECTION_TOKEN, + ...commandClasses, + upgradeAllCommand, + ], + }; + } +} 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 c40371740..b01d110a2 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 @@ -4,11 +4,11 @@ import chalk from 'chalk'; import { Command } from 'nest-commander'; import { In, Repository } from 'typeorm'; -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; import { isCommandLogger } from 'src/database/commands/logger'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.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'; @@ -20,7 +20,7 @@ import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.work name: 'upgrade-0.42:fix-body-v2-view-field-position', description: 'Make bodyV2 field position to match body field position', }) -export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesCommandRunner { +export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -32,9 +32,9 @@ export class FixBodyV2ViewFieldPositionCommand extends ActiveWorkspacesCommandRu super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: ActiveWorkspacesCommandOptions, + options: ActiveWorkspacesMigrationCommandOptions, workspaceIds: string[], ): Promise { this.logger.log('Running command to fix bodyV2 field position'); 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 29579c153..142c3fb84 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 @@ -1,25 +1,26 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; -import { Command } from 'nest-commander'; import { Repository } from 'typeorm'; -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; import { CommandLogger } from 'src/database/commands/logger'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { settings } from 'src/engine/constants/settings'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity'; import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity'; -@Command({ - name: 'upgrade-0.42:limit-amount-of-view-field', +@MigrationCommand({ + name: 'limit-amount-of-view-field', description: 'Limit amount of view field.', + version: '0.42', }) -export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner { +export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesMigrationCommandRunner { protected readonly logger: CommandLogger; constructor( @@ -35,7 +36,7 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner this.logger.setVerbose(false); } - async execute(workspaceId: string, dryRun?: boolean): Promise { + async runOnWorkspace(workspaceId: string, dryRun?: boolean): Promise { this.logger.log( `Processing workspace ${workspaceId} for view field limitation`, ); @@ -94,9 +95,9 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner } } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: ActiveWorkspacesCommandOptions, + options: ActiveWorkspacesMigrationCommandOptions, workspaceIds: string[], ): Promise { this.logger.log(`Running limit-amount-of-view-field command`); @@ -107,7 +108,7 @@ export class LimitAmountOfViewFieldCommand extends ActiveWorkspacesCommandRunner for (const [index, workspaceId] of workspaceIds.entries()) { try { - await this.execute(workspaceId, options?.dryRun); + await this.runOnWorkspace(workspaceId, options?.dryRun); this.logger.verbose( `Processed workspace: ${workspaceId} (${index + 1}/${ workspaceIds.length 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 f3e9352c8..a051fe76c 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 @@ -2,13 +2,16 @@ import { InjectRepository } from '@nestjs/typeorm'; import { ServerBlockNoteEditor } from '@blocknote/server-util'; import chalk from 'chalk'; -import { Command, Option } from 'nest-commander'; +import { Option } from 'nest-commander'; import { FieldMetadataType, isDefined } from 'twenty-shared'; import { Repository } from 'typeorm'; -import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; import { isCommandLogger } from 'src/database/commands/logger'; -import { Upgrade042CommandOptions } from 'src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -55,12 +58,19 @@ type ProcessRichTextFieldsArgs = { workspaceId: string; }; -@Command({ - name: 'upgrade-0.42:migrate-rich-text-field', +type MigrateRichTextFieldCommandOptions = + ActiveWorkspacesMigrationCommandOptions & { + force?: boolean; + }; + +@MigrationCommand({ + name: 'migrate-rich-text-field', description: 'Migrate RICH_TEXT fields to new composite structure', + version: '0.42', }) -export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { - private options: Upgrade042CommandOptions; +export class MigrateRichTextFieldCommand extends ActiveWorkspacesMigrationCommandRunner { + private options: MigrateRichTextFieldCommandOptions; + constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -89,9 +99,9 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { return val ?? false; } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: Upgrade042CommandOptions, + options: MigrateRichTextFieldCommandOptions, workspaceIds: string[], ): Promise { this.logger.log( @@ -343,7 +353,7 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { }); richTextFieldsWithHasCreatedColumns.push({ - hasCreatedColumns, + hasCreatedColumns: hasCreatedColumns ?? false, richTextField, objectMetadata, }); 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 fa943474d..48d604a08 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 @@ -1,25 +1,26 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; -import { Command } from 'nest-commander'; import { FieldMetadataType } from 'twenty-shared'; import { IsNull, Repository } from 'typeorm'; -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; import { CommandLogger } from 'src/database/commands/logger'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; 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'; -@Command({ - name: 'upgrade-0.42:standardization-of-actor-composite-context-type', +@MigrationCommand({ + name: 'standardization-of-actor-composite-context-type', description: 'Add context to actor composite type.', + version: '0.42', }) -export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWorkspacesCommandRunner { +export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWorkspacesMigrationCommandRunner { protected readonly logger; constructor( @@ -39,9 +40,9 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor this.logger.setVerbose(false); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: ActiveWorkspacesCommandOptions, + options: ActiveWorkspacesMigrationCommandOptions, workspaceIds: string[], ): Promise { this.logger.log(`Running add-context-to-actor-composite-type command`); @@ -52,7 +53,7 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor for (const [index, workspaceId] of workspaceIds.entries()) { try { - await this.execute(workspaceId, options?.dryRun); + await this.runOnWorkspace(workspaceId, options?.dryRun); this.logger.verbose( `[${index + 1}/${workspaceIds.length}] Added for workspace: ${workspaceId}`, ); @@ -62,7 +63,10 @@ export class StandardizationOfActorCompositeContextTypeCommand extends ActiveWor } } - private async execute(workspaceId: string, dryRun = false): Promise { + private async runOnWorkspace( + workspaceId: string, + dryRun = false, + ): Promise { this.logger.verbose(`Adding for workspace: ${workspaceId}`); const actorFields = await this.fieldMetadataRepository.find({ where: { 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 deleted file mode 100644 index 21d6ada90..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command, Option } 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 { FixBodyV2ViewFieldPositionCommand } from 'src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command'; -import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field'; -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 = { - force: boolean; -}; -export type Upgrade042CommandOptions = BaseCommandOptions & - Upgrade042CommandCustomOptions; -@Command({ - name: 'upgrade-0.42', - description: 'Upgrade to 0.42', -}) -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, twentyORMGlobalManager); - } - - @Option({ - flags: '-f, --force [boolean]', - description: - 'Force RICH_TEXT_FIELD value update even if column migration has already be run', - required: false, - }) - parseForceValue(val?: boolean): boolean { - return val ?? false; - } - - async executeActiveWorkspacesCommand( - passedParam: string[], - options: Upgrade042CommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to upgrade to 0.42'); - - await this.migrateRichTextFieldCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.fixBodyV2ViewFieldPositionCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.limitAmountOfViewFieldCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.syncWorkspaceMetadataCommand.executeActiveWorkspacesCommand( - passedParam, - { - ...options, - force: true, - }, - workspaceIds, - ); - - await this.standardizationOfActorCompositeContextType.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.module.ts index c923f83a5..7e422ec79 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-42/0-42-upgrade-version.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { MigrationCommandModule } from 'src/database/commands/migration-command/miration-command.module'; import { FixBodyV2ViewFieldPositionCommand } from 'src/database/commands/upgrade-version/0-42/0-42-fix-body-v2-view-field-position.command'; import { LimitAmountOfViewFieldCommand } from 'src/database/commands/upgrade-version/0-42/0-42-limit-amount-of-view-field'; 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 { UpgradeTo0_42Command } from 'src/database/commands/upgrade-version/0-42/0-42-upgrade-version.command'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -19,24 +19,27 @@ import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manage @Module({ imports: [ - TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'), - TypeOrmModule.forFeature( - [ObjectMetadataEntity, FieldMetadataEntity], - 'metadata', - ), - WorkspaceSyncMetadataCommandsModule, - WorkspaceMigrationRunnerModule, - WorkspaceMigrationModule, - WorkspaceMetadataVersionModule, - WorkspaceDataSourceModule, - FeatureFlagModule, - ], - providers: [ - UpgradeTo0_42Command, - MigrateRichTextFieldCommand, - FixBodyV2ViewFieldPositionCommand, - LimitAmountOfViewFieldCommand, - StandardizationOfActorCompositeContextTypeCommand, + MigrationCommandModule.register('0.42', { + imports: [ + TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'), + TypeOrmModule.forFeature( + [ObjectMetadataEntity, FieldMetadataEntity], + 'metadata', + ), + WorkspaceSyncMetadataCommandsModule, + WorkspaceMigrationRunnerModule, + WorkspaceMigrationModule, + WorkspaceMetadataVersionModule, + WorkspaceDataSourceModule, + FeatureFlagModule, + ], + providers: [ + MigrateRichTextFieldCommand, + FixBodyV2ViewFieldPositionCommand, + LimitAmountOfViewFieldCommand, + StandardizationOfActorCompositeContextTypeCommand, + ], + }), ], }) export class UpgradeTo0_42CommandModule {} 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 cf2025357..01558fed3 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 @@ -1,15 +1,15 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; -import { Command } from 'nest-commander'; import { Repository } from 'typeorm'; import { v4 } from 'uuid'; -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; import { isCommandLogger } from 'src/database/commands/logger'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -24,11 +24,12 @@ import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/vie import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity'; import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity'; -@Command({ - name: 'upgrade-0.43:add-tasks-assigned-to-me-view', +@MigrationCommand({ + name: 'add-tasks-assigned-to-me-view', description: 'Add tasks assigned to me view', + version: '0.43', }) -export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesCommandRunner { +export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -42,9 +43,9 @@ export class AddTasksAssignedToMeViewCommand extends ActiveWorkspacesCommandRunn super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: ActiveWorkspacesCommandOptions, + options: ActiveWorkspacesMigrationCommandOptions, workspaceIds: string[], ): Promise { this.logger.log('Running command to create many to one relations'); diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command.ts new file mode 100644 index 000000000..752a942a9 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command.ts @@ -0,0 +1,169 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { FieldMetadataType } from 'twenty-shared'; +import { In, Repository } from 'typeorm'; + +import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; + +import { isCommandLogger } from 'src/database/commands/logger'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { + RelationDirection, + deduceRelationDirection, +} from 'src/engine/utils/deduce-relation-direction.util'; +import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; + +@MigrationCommand({ + name: 'migrate-relations-to-field-metadata', + description: 'Migrate relations to field metadata', + version: '0.43', +}) +export class MigrateRelationsToFieldMetadataCommand extends ActiveWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + async runMigrationCommandOnActiveWorkspaces( + _passedParam: string[], + options: ActiveWorkspacesMigrationCommandOptions, + 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: In([FieldMetadataType.RELATION, FieldMetadataType.UUID]), + }, + relations: ['fromRelationMetadata', 'toRelationMetadata'], + }); + + if (!fieldMetadataCollection.length) { + this.logger.log( + chalk.yellow( + `No relation field metadata found for workspace ${workspaceId}.`, + ), + ); + + return; + } + + const joinColumnFieldMetadataCollection = fieldMetadataCollection.filter( + (fieldMetadata) => + isFieldMetadataOfType(fieldMetadata, FieldMetadataType.UUID), + // TODO: Fix this, it's working in other places but not here + ) as FieldMetadataEntity[]; + + const fieldMetadataToUpdateCollection = fieldMetadataCollection + .filter((fieldMetadata) => + isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION), + ) + .map((fieldMetadata) => + this.updateRelationFieldMetadata( + joinColumnFieldMetadataCollection, + // TODO: Fix this, it's working in other places but not here + fieldMetadata as FieldMetadataEntity, + ), + ); + + 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 updateRelationFieldMetadata( + joinColumnFieldMetadataCollection: FieldMetadataEntity[], + fieldMetadata: FieldMetadataEntity, + ): FieldMetadataEntity { + const relationMetadata = + fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata; + const joinColumnFieldMetadata = joinColumnFieldMetadataCollection.find( + (joinColumnFieldMetadata) => + // We're deducing the field based on the name of the relation field + // This is not the best way to do this but we don't have a better way + joinColumnFieldMetadata.name === `${fieldMetadata.name}Id`, + ); + + 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, + joinColumnName: joinColumnFieldMetadata?.name, + }, + relationTargetFieldMetadataId, + relationTargetObjectMetadataId, + }; + } +} 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 5ffc4a476..42d9c1f2a 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 @@ -1,13 +1,13 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; -import { Command } from 'nest-commander'; import { Repository } from 'typeorm'; import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -19,11 +19,12 @@ import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/wo 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'; -@Command({ - name: 'upgrade-0.43:migrate-search-vector-on-note-and-task-entities', +@MigrationCommand({ + name: 'migrate-search-vector-on-note-and-task-entities', description: 'Migrate search vector on note and task entities', + version: '0.43', }) -export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorkspacesCommandRunner { +export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -39,9 +40,9 @@ export class MigrateSearchVectorOnNoteAndTaskEntitiesCommand extends ActiveWorks super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], - options: ActiveWorkspacesCommandOptions, + options: ActiveWorkspacesMigrationCommandOptions, workspaceIds: string[], ): Promise { this.logger.log( 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 dbe6f3a9a..8ce93b594 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 @@ -1,59 +1,34 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; -import { Command } from 'nest-commander'; import { In, Repository } from 'typeorm'; -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; +import { BatchActiveWorkspacesMigrationCommandRunner } from 'src/database/commands/migration-command/batch-active-workspaces-migration-command.runner'; +import { MigrationCommand } from 'src/database/commands/migration-command/decorators/migration-command.decorator'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.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 { 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 { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; import { ViewOpenRecordInType } from 'src/modules/view/standard-objects/view.workspace-entity'; -@Command({ - name: 'upgrade-0.43:update-default-view-record-opening-on-workflow-objects', +@MigrationCommand({ + name: 'update-default-view-record-opening-on-workflow-objects', description: 'Update default view record opening on workflow objects to record page', + version: '0.43', }) -export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends ActiveWorkspacesCommandRunner { +export class UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand extends BatchActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @InjectRepository(ObjectMetadataEntity, 'metadata') protected readonly objectMetadataRepository: Repository, - private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, - private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) { super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( - _passedParam: string[], - _options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log( - 'Running command to update default view record opening on workflow objects to record page', - ); - - this.processEachWorkspaceWithWorkspaceDataSource( - workspaceIds, - async ({ workspaceId, index, total }) => { - await this.processWorkspace(workspaceId, index, total); - }, - ); - - this.logger.log(chalk.green('Command completed!')); - } - - async processWorkspace( + async runMigrationCommandOnWorkspace( workspaceId: string, index: number, total: number, 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 deleted file mode 100644 index e625af194..000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command.ts +++ /dev/null @@ -1,64 +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 { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type'; -import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command'; -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', - description: 'Upgrade to 0.43', -}) -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, twentyORMGlobalManager); - } - - async executeActiveWorkspacesCommand( - passedParam: string[], - options: BaseCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to upgrade to 0.43'); - - await this.addTasksAssignedToMeViewCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.migrateSearchVectorOnNoteAndTaskEntitiesCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - await this.updateDefaultViewRecordOpeningOnWorkflowObjectsCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - - // Note: Introduced in 0.42, ran manually on prod. Introduced to self-host globally on 0.43 - await this.standardizationOfActorCompositeContextTypeCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.module.ts index b3872a334..579c101e7 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-43/0-43-upgrade-version.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { MigrationCommandModule } from 'src/database/commands/migration-command/miration-command.module'; import { StandardizationOfActorCompositeContextTypeCommand } from 'src/database/commands/upgrade-version/0-42/0-42-standardization-of-actor-composite-context-type'; import { AddTasksAssignedToMeViewCommand } from 'src/database/commands/upgrade-version/0-43/0-43-add-tasks-assigned-to-me-view.command'; +import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-43/0-43-migrate-relations-to-field-metadata.command'; 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 { UpgradeTo0_43Command } from 'src/database/commands/upgrade-version/0-43/0-43-upgrade-version.command'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -17,22 +18,26 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor @Module({ imports: [ - TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'), - TypeOrmModule.forFeature( - [ObjectMetadataEntity, FieldMetadataEntity], - 'metadata', - ), - SearchModule, - WorkspaceMigrationRunnerModule, - WorkspaceMigrationModule, - WorkspaceMetadataVersionModule, - ], - providers: [ - UpgradeTo0_43Command, - AddTasksAssignedToMeViewCommand, - MigrateSearchVectorOnNoteAndTaskEntitiesCommand, - UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand, - StandardizationOfActorCompositeContextTypeCommand, + MigrationCommandModule.register('0.43', { + imports: [ + TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'), + TypeOrmModule.forFeature( + [ObjectMetadataEntity, FieldMetadataEntity], + 'metadata', + ), + SearchModule, + WorkspaceMigrationRunnerModule, + WorkspaceMigrationModule, + WorkspaceMetadataVersionModule, + ], + providers: [ + AddTasksAssignedToMeViewCommand, + MigrateSearchVectorOnNoteAndTaskEntitiesCommand, + UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand, + StandardizationOfActorCompositeContextTypeCommand, + MigrateRelationsToFieldMetadataCommand, + ], + }), ], }) export class UpgradeTo0_43CommandModule {} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts index b1005ccda..f28bad5fc 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; +import { FieldMetadataType } from 'twenty-shared'; import { DataSource, FindOptionsRelations, @@ -22,7 +23,7 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; -import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util'; +import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; @Injectable() export class ProcessNestedRelationsV2Helper { @@ -96,7 +97,9 @@ export class ProcessNestedRelationsV2Helper { const sourceFieldMetadata = parentObjectMetadataItem.fieldsByName[sourceFieldName]; - if (!isRelationFieldMetadata(sourceFieldMetadata)) { + if ( + !isFieldMetadataOfType(sourceFieldMetadata, FieldMetadataType.RELATION) + ) { // TODO: Maybe we should throw an error here ? return; } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts index f466f075b..4c061b53a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; -import { isDefined } from 'twenty-shared'; +import { FieldMetadataType, isDefined } from 'twenty-shared'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value'; @@ -21,7 +21,7 @@ import { CompositeInputTypeDefinitionFactory } from 'src/engine/api/graphql/work import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; -import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util'; +import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; // TODO: find a way to prevent conflict between handlers executing logic on object relations // And this factory that is also executing logic on object relations @@ -151,7 +151,9 @@ export class QueryResultGettersFactory { objectMetadataMapItem.fieldsByName[recordFieldName], ) .filter(isDefined) - .filter((fieldMetadata) => isRelationFieldMetadata(fieldMetadata)); + .filter((fieldMetadata) => + isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION), + ); const relationFieldsProcessedMap = {} as Record< string, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/factories/extend-object-type-definition-v2.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/factories/extend-object-type-definition-v2.factory.ts index 036a3b338..c966b38d0 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/factories/extend-object-type-definition-v2.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/factories/extend-object-type-definition-v2.factory.ts @@ -5,6 +5,7 @@ import { GraphQLFieldConfigMap, GraphQLObjectType, } from 'graphql'; +import { FieldMetadataType } from 'twenty-shared'; import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface'; import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; @@ -14,7 +15,7 @@ import { RelationTypeV2Factory } from 'src/engine/api/graphql/workspace-schema-b import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage'; import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util'; import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field'; -import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util'; +import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; import { ArgsFactory } from './args.factory'; @@ -107,7 +108,7 @@ export class ExtendObjectTypeDefinitionV2Factory { for (const fieldMetadata of objectMetadata.fields) { // Ignore non-relation fields as they are already defined - if (!isRelationFieldMetadata(fieldMetadata)) { + if (!isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION)) { continue; } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index c53658ffc..37c17d8a1 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -45,7 +45,7 @@ export interface TypeOptions { isArray?: boolean; arrayDepth?: number; defaultValue?: T; - settings?: FieldMetadataSettings; + settings?: FieldMetadataSettings; isIdField?: boolean; } @@ -55,7 +55,7 @@ const StringArrayScalarType = new GraphQLList(GraphQLString); export class TypeMapperService { mapToScalarType( fieldMetadataType: FieldMetadataType, - settings?: FieldMetadataSettings, + settings?: FieldMetadataSettings, isIdField?: boolean, ): GraphQLScalarType | undefined { if (isIdField || settings?.isForeignKey) { @@ -90,7 +90,7 @@ export class TypeMapperService { mapToFilterType( fieldMetadataType: FieldMetadataType, - settings?: FieldMetadataSettings, + settings?: FieldMetadataSettings, isIdField?: boolean, ): GraphQLInputObjectType | GraphQLScalarType | undefined { if (isIdField || settings?.isForeignKey) { 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 ba2bdc334..0ce2cf8ba 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 @@ -7,22 +7,22 @@ import { Command } from 'nest-commander'; import { Repository } from 'typeorm'; import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; 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 {} + extends ActiveWorkspacesMigrationCommandOptions {} @Command({ name: 'billing:sync-customer-data', description: 'Sync customer data from Stripe for all active workspaces', }) -export class BillingSyncCustomerDataCommand extends ActiveWorkspacesCommandRunner { +export class BillingSyncCustomerDataCommand extends ActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -34,7 +34,7 @@ export class BillingSyncCustomerDataCommand extends ActiveWorkspacesCommandRunne super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], options: SyncCustomerDataCommandOptions, workspaceIds: string[], diff --git a/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-plans-data.command.ts b/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-plans-data.command.ts index 598321e3e..c5de0ea44 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-plans-data.command.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/commands/billing-sync-plans-data.command.ts @@ -7,9 +7,9 @@ import Stripe from 'stripe'; import { Repository } from 'typeorm'; import { - BaseCommandOptions, - BaseCommandRunner, -} from 'src/database/commands/base.command'; + MigrationCommandOptions, + MigrationCommandRunner, +} from 'src/database/commands/migration-command/migration-command.runner'; import { BillingMeter } from 'src/engine/core-modules/billing/entities/billing-meter.entity'; import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-price.entity'; import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity'; @@ -25,7 +25,7 @@ import { transformStripeProductToDatabaseProduct } from 'src/engine/core-modules description: 'Fetches from stripe the plans data (meter, product and price) and upserts it into the database', }) -export class BillingSyncPlansDataCommand extends BaseCommandRunner { +export class BillingSyncPlansDataCommand extends MigrationCommandRunner { private readonly batchSize = 5; constructor( @InjectRepository(BillingPrice, 'core') @@ -43,7 +43,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner { private async upsertMetersRepositoryData( meters: Stripe.Billing.Meter[], - options: BaseCommandOptions, + options: MigrationCommandOptions, ) { meters.map(async (meter) => { try { @@ -64,7 +64,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner { private async upsertProductRepositoryData( product: Stripe.Product, - options: BaseCommandOptions, + options: MigrationCommandOptions, ) { try { if (!options.dryRun) { @@ -83,7 +83,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner { private async getBillingPrices( products: Stripe.Product[], - options: BaseCommandOptions, + options: MigrationCommandOptions, ): Promise { return await Promise.all( products.map(async (product) => { @@ -113,7 +113,7 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner { private async processBillingPricesByProductBatches( products: Stripe.Product[], - options: BaseCommandOptions, + options: MigrationCommandOptions, ) { const prices: Stripe.Price[][] = []; @@ -135,9 +135,9 @@ export class BillingSyncPlansDataCommand extends BaseCommandRunner { return prices; } - override async executeBaseCommand( + override async runMigrationCommand( passedParams: string[], - options: BaseCommandOptions, + options: MigrationCommandOptions, ): Promise { const billingMeters = await this.stripeBillingMeterService.getAllMeters(); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts index 0b93aa36b..1e7b20961 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts @@ -64,9 +64,7 @@ registerEnumType(FieldMetadataType, { @Relation('object', () => ObjectMetadataDTO, { nullable: true, }) -export class FieldMetadataDTO< - T extends FieldMetadataType | 'default' = 'default', -> { +export class FieldMetadataDTO { @IsUUID() @IsNotEmpty() @IDField(() => UUIDScalarType) @@ -75,7 +73,7 @@ export class FieldMetadataDTO< @IsEnum(FieldMetadataType) @IsNotEmpty() @Field(() => FieldMetadataType) - type: FieldMetadataType; + type: T; @IsString() @IsNotEmpty() diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts index caf2ef329..28f8ae9ef 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts @@ -44,7 +44,7 @@ class TextSettingsValidation { @Injectable() export class FieldMetadataValidationService< - T extends FieldMetadataType | 'default' = 'default', + T extends FieldMetadataType = FieldMetadataType, > { constructor() {} diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts index 53b746388..d34f5e5f6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts @@ -36,7 +36,7 @@ import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-met 'relationTargetObjectMetadataId', ]) export class FieldMetadataEntity< - T extends FieldMetadataType | 'default' = 'default', + T extends FieldMetadataType = FieldMetadataType, > implements FieldMetadataInterface { @PrimaryGeneratedColumn('uuid') @@ -59,7 +59,7 @@ export class FieldMetadataEntity< nullable: false, type: 'varchar', }) - type: FieldMetadataType; + type: T; @Column({ nullable: false }) name: string; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 5196dac24..ae822c292 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -1,4 +1,4 @@ -import { FieldMetadataType } from 'twenty-shared'; +import { FieldMetadataType, IsExactly } from 'twenty-shared'; import { FieldMetadataDefaultActor, @@ -60,17 +60,14 @@ export type FieldMetadataFunctionDefaultValue = ExtractValueType< FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction >; -type DefaultValueByFieldMetadata = [ - T, -] extends [keyof FieldMetadataDefaultValueMapping] - ? ExtractValueType | null - : T extends 'default' - ? ExtractValueType> | null - : never; - export type FieldMetadataDefaultValue< - T extends FieldMetadataType | 'default' = 'default', -> = DefaultValueByFieldMetadata; + T extends FieldMetadataType = FieldMetadataType, +> = + IsExactly extends true + ? ExtractValueType> | null + : T extends keyof FieldMetadataDefaultValueMapping + ? ExtractValueType | null + : never; type FieldMetadataDefaultValueExtractedTypes = { [K in keyof FieldMetadataDefaultValueMapping]: ExtractValueType< diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface.ts index b801612f6..f5d871104 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface.ts @@ -1,4 +1,4 @@ -import { FieldMetadataType } from 'twenty-shared'; +import { FieldMetadataType, IsExactly } from 'twenty-shared'; import { FieldMetadataComplexOption, @@ -11,13 +11,11 @@ type FieldMetadataOptionsMapping = { [FieldMetadataType.MULTI_SELECT]: FieldMetadataComplexOption[]; }; -type OptionsByFieldMetadata = - T extends keyof FieldMetadataOptionsMapping - ? FieldMetadataOptionsMapping[T] - : T extends 'default' - ? FieldMetadataDefaultOption[] | FieldMetadataComplexOption[] - : never; - export type FieldMetadataOptions< - T extends FieldMetadataType | 'default' = 'default', -> = OptionsByFieldMetadata; + T extends FieldMetadataType = FieldMetadataType, +> = + IsExactly extends true + ? FieldMetadataDefaultOption[] | FieldMetadataComplexOption[] + : T extends keyof FieldMetadataOptionsMapping + ? FieldMetadataOptionsMapping[T] + : never; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts index 230cfcdaa..38db567f8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts @@ -1,4 +1,4 @@ -import { FieldMetadataType } from 'twenty-shared'; +import { FieldMetadataType, IsExactly } from 'twenty-shared'; import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; @@ -36,6 +36,7 @@ export type FieldMetadataDateTimeSettings = { export type FieldMetadataRelationSettings = { relationType: RelationType; onDelete?: RelationOnDeleteAction; + joinColumnName?: string; }; type FieldMetadataSettingsMapping = { @@ -46,13 +47,11 @@ type FieldMetadataSettingsMapping = { [FieldMetadataType.RELATION]: FieldMetadataRelationSettings; }; -type SettingsByFieldMetadata = - T extends keyof FieldMetadataSettingsMapping - ? FieldMetadataSettingsMapping[T] & FieldMetadataDefaultSettings - : T extends 'default' - ? FieldMetadataDefaultSettings - : never; - export type FieldMetadataSettings< - T extends FieldMetadataType | 'default' = 'default', -> = SettingsByFieldMetadata; + T extends FieldMetadataType = FieldMetadataType, +> = + IsExactly extends true + ? FieldMetadataDefaultSettings + : T extends keyof FieldMetadataSettingsMapping + ? FieldMetadataSettingsMapping[T] & FieldMetadataDefaultSettings + : never; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface.ts index 055d8de81..e69f3ed77 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface.ts @@ -9,10 +9,10 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; export interface FieldMetadataInterface< - T extends FieldMetadataType | 'default' = 'default', + T extends FieldMetadataType = FieldMetadataType, > { id: string; - type: FieldMetadataType; + type: T; name: string; label: string; defaultValue?: FieldMetadataDefaultValue; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/compute-column-name.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/compute-column-name.util.ts index 53958fa1c..d9da214c8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/compute-column-name.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/compute-column-name.util.ts @@ -21,12 +21,12 @@ export function computeColumnName( fieldName: string, options?: ComputeColumnNameOptions, ): string; -export function computeColumnName( +export function computeColumnName( fieldMetadata: FieldMetadataInterface, ioptions?: ComputeColumnNameOptions, ): string; // TODO: If we need to implement custom name logic for columns, we can do it here -export function computeColumnName( +export function computeColumnName( fieldMetadataOrFieldName: FieldMetadataInterface | string, options?: ComputeColumnNameOptions, ): string { @@ -51,15 +51,11 @@ export function computeCompositeColumnName( fieldName: string, compositeProperty: CompositeProperty, ): string; -export function computeCompositeColumnName< - T extends FieldMetadataType | 'default', ->( +export function computeCompositeColumnName( fieldMetadata: FieldTypeAndNameMetadata | FieldMetadataInterface, compositeProperty: CompositeProperty, ): string; -export function computeCompositeColumnName< - T extends FieldMetadataType | 'default', ->( +export function computeCompositeColumnName( fieldMetadataOrFieldName: | FieldTypeAndNameMetadata | FieldMetadataInterface diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/dtos/create-object.input.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/dtos/create-object.input.ts index ef365e647..579145bbd 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/dtos/create-object.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/dtos/create-object.input.ts @@ -67,9 +67,7 @@ export class CreateObjectInput { @IsOptional() @Field(() => GraphQLJSON, { nullable: true }) - primaryKeyFieldMetadataSettings?: FieldMetadataSettings< - FieldMetadataType | 'default' - >; + primaryKeyFieldMetadataSettings?: FieldMetadataSettings; @IsBoolean() @IsOptional() diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts index fbec4faf7..b7b896712 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts @@ -73,7 +73,7 @@ export class ObjectMetadataRelationService { createdObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, relationObjectMetadataStandardId: string, ) { @@ -109,7 +109,7 @@ export class ObjectMetadataRelationService { relatedObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, ) { return this.fieldMetadataRepository.save([ @@ -340,7 +340,7 @@ export class ObjectMetadataRelationService { relatedObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, isUpdate = false, ) { diff --git a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts index 537b03e5b..6d5510169 100644 --- a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts @@ -147,7 +147,7 @@ export class RelationMetadataService extends TypeOrmQueryService, + deletedAtFieldMetadata as FieldMetadataEntity, ], false, false, @@ -451,7 +451,7 @@ export class RelationMetadataService extends TypeOrmQueryService, + deletedAtFieldMetadata as FieldMetadataEntity, ], ); @@ -570,7 +570,7 @@ export class RelationMetadataService extends TypeOrmQueryService | null, + deletedAtFieldMetadata?: FieldMetadataEntity | null, ) { if (!isDefined(deletedAtFieldMetadata)) { throw new RelationMetadataException( diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service.ts index 0ae9bb96f..00eb54b3f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service.ts @@ -36,7 +36,7 @@ export class RemoteTableRelationsService { workspaceId: string, remoteObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, objectPrimaryKeyColumnType?: string, ) { @@ -150,7 +150,7 @@ export class RemoteTableRelationsService { createdObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, ) { const attachmentObjectMetadata = @@ -190,7 +190,7 @@ export class RemoteTableRelationsService { createdObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, ) { const timelineActivityObjectMetadata = @@ -230,7 +230,7 @@ export class RemoteTableRelationsService { createdObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: - | FieldMetadataSettings + | FieldMetadataSettings | undefined, ) { const favoriteObjectMetadata = diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util.ts index 2799da011..1494c744d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util.ts @@ -37,12 +37,12 @@ export const mapUdtNameToFieldSettings = ( case 'int4': return { dataType: NumberDataType.INT, - } satisfies FieldMetadataSettings; + } as FieldMetadataSettings; case 'int8': case 'bigint': return { dataType: NumberDataType.BIGINT, - } satisfies FieldMetadataSettings; + } as FieldMetadataSettings; default: return undefined; } diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory.ts index 1572fd9a2..176719650 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory.ts @@ -18,9 +18,8 @@ import { WorkspaceMigrationExceptionCode, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception'; -export class ColumnActionAbstractFactory< - T extends FieldMetadataType | 'default', -> implements WorkspaceColumnActionFactory +export class ColumnActionAbstractFactory + implements WorkspaceColumnActionFactory { protected readonly logger = new Logger(ColumnActionAbstractFactory.name); diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-factory.interface.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-factory.interface.ts index f8a2f034f..66a738da5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-factory.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/interfaces/workspace-column-action-factory.interface.ts @@ -8,9 +8,7 @@ import { WorkspaceMigrationColumnActionType, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; -export interface WorkspaceColumnActionFactory< - T extends FieldMetadataType | 'default', -> { +export interface WorkspaceColumnActionFactory { create( action: | WorkspaceMigrationColumnActionType.CREATE diff --git a/packages/twenty-server/src/engine/seeder/metadata-seeds/survey-results-metadata-seeds.ts b/packages/twenty-server/src/engine/seeder/metadata-seeds/survey-results-metadata-seeds.ts index 14a76da5f..cc71a66d2 100644 --- a/packages/twenty-server/src/engine/seeder/metadata-seeds/survey-results-metadata-seeds.ts +++ b/packages/twenty-server/src/engine/seeder/metadata-seeds/survey-results-metadata-seeds.ts @@ -1,7 +1,6 @@ +import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto'; import { - FieldMetadataNumberSettings, - FieldMetadataTextSettings, - NumberDataType, + NumberDataType } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; import { ObjectMetadataSeed } from 'src/engine/seeder/interfaces/object-metadata-seed'; @@ -22,8 +21,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = { dataType: NumberDataType.FLOAT, decimals: 3, type: 'number', - } as FieldMetadataNumberSettings, - }, + }, + } as FieldMetadataDTO, { type: FieldMetadataType.NUMBER, label: 'Percentage of completion (Float 3 decimals + percentage)', @@ -32,8 +31,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = { dataType: NumberDataType.FLOAT, decimals: 6, type: 'percentage', - } as FieldMetadataNumberSettings, - }, + }, + } as FieldMetadataDTO, { type: FieldMetadataType.NUMBER, label: 'Participants (Int)', @@ -41,8 +40,8 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = { settings: { dataType: NumberDataType.INT, type: 'number', - } as FieldMetadataNumberSettings, - }, + }, + } as FieldMetadataDTO, { type: FieldMetadataType.NUMBER, label: 'Average estimated number of atoms in the universe (BigInt)', @@ -50,23 +49,23 @@ export const SURVEY_RESULTS_METADATA_SEEDS: ObjectMetadataSeed = { settings: { dataType: NumberDataType.BIGINT, type: 'number', - } as FieldMetadataNumberSettings, - }, + }, + } as FieldMetadataDTO, { type: FieldMetadataType.TEXT, label: 'Comments (Max 5 rows)', name: 'comments', settings: { displayedMaxRows: 5, - } as FieldMetadataTextSettings, - }, + }, + } as FieldMetadataDTO, { type: FieldMetadataType.TEXT, label: 'Short notes (Max 1 row)', name: 'shortNotes', settings: { displayedMaxRows: 1, - } as FieldMetadataTextSettings, - }, + }, + } as FieldMetadataDTO, ], }; diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts index ac07bce1f..aafe1beda 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts @@ -11,7 +11,7 @@ import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args import { TypedReflect } from 'src/utils/typed-reflect'; export interface WorkspaceFieldOptions< - T extends FieldMetadataType | 'default', + T extends FieldMetadataType = FieldMetadataType, > { standardId: string; type: T; diff --git a/packages/twenty-server/src/engine/utils/is-field-metadata-of-type.util.ts b/packages/twenty-server/src/engine/utils/is-field-metadata-of-type.util.ts new file mode 100644 index 000000000..95da6241f --- /dev/null +++ b/packages/twenty-server/src/engine/utils/is-field-metadata-of-type.util.ts @@ -0,0 +1,28 @@ +import { FieldMetadataType } from 'twenty-shared'; + +import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; + +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; + +export function isFieldMetadataOfType< + Field extends FieldMetadataInterface, + Type extends FieldMetadataType, +>( + fieldMetadata: Field, + type: Type, +): fieldMetadata is Field & FieldMetadataInterface; +export function isFieldMetadataOfType< + Field extends FieldMetadataEntity, + Type extends FieldMetadataType, +>( + fieldMetadata: Field, + type: Type, +): fieldMetadata is Field & FieldMetadataEntity; +export function isFieldMetadataOfType< + Field extends + | FieldMetadataInterface + | FieldMetadataEntity, + Type extends FieldMetadataType, +>(fieldMetadata: Field, type: Type): boolean { + return fieldMetadata.type === type; +} diff --git a/packages/twenty-server/src/engine/utils/is-relation-field-metadata.util.ts b/packages/twenty-server/src/engine/utils/is-relation-field-metadata.util.ts deleted file mode 100644 index 9dada2530..000000000 --- a/packages/twenty-server/src/engine/utils/is-relation-field-metadata.util.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { FieldMetadataType } from 'twenty-shared'; - -import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; - -export const isRelationFieldMetadata = ( - fieldMetadata: FieldMetadataInterface<'default' | FieldMetadataType.RELATION>, -): fieldMetadata is FieldMetadataInterface => { - return fieldMetadata.type === FieldMetadataType.RELATION; -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts index 97182497d..aa8880336 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.command.ts @@ -5,9 +5,9 @@ import { WorkspaceActivationStatus } from 'twenty-shared'; import { In, Repository } from 'typeorm'; import { - BaseCommandOptions, - BaseCommandRunner, -} from 'src/database/commands/base.command'; + MigrationCommandOptions, + MigrationCommandRunner, +} from 'src/database/commands/migration-command/migration-command.runner'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace-cleaner/services/cleaner.workspace-service'; @@ -15,7 +15,7 @@ import { CleanerWorkspaceService } from 'src/engine/workspace-manager/workspace- name: 'workspace:clean', description: 'Clean suspended workspace', }) -export class CleanSuspendedWorkspacesCommand extends BaseCommandRunner { +export class CleanSuspendedWorkspacesCommand extends MigrationCommandRunner { private workspaceIds: string[] = []; constructor( @@ -50,9 +50,9 @@ export class CleanSuspendedWorkspacesCommand extends BaseCommandRunner { return suspendedWorkspaces.map((workspace) => workspace.id); } - override async executeBaseCommand( + override async runMigrationCommand( _passedParams: string[], - options: BaseCommandOptions, + options: MigrationCommandOptions, ): Promise { const { dryRun } = options; 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 c8372d3da..219c196f4 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 @@ -3,7 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Command, Option } from 'nest-commander'; import { Repository } from 'typeorm'; -import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; +import { + ActiveWorkspacesMigrationCommandOptions, + ActiveWorkspacesMigrationCommandRunner, +} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner'; 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'; @@ -12,18 +15,16 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service'; -// TODO: implement dry-run -interface RunWorkspaceMigrationsOptions { - dryRun?: boolean; +interface RunWorkspaceMigrationsOptions + extends ActiveWorkspacesMigrationCommandOptions { force?: boolean; - workspaceId?: string; } @Command({ name: 'workspace:sync-metadata', description: 'Sync metadata', }) -export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner { +export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesMigrationCommandRunner { constructor( @InjectRepository(Workspace, 'core') protected readonly workspaceRepository: Repository, @@ -36,7 +37,7 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner super(workspaceRepository, twentyORMGlobalManager); } - async executeActiveWorkspacesCommand( + async runMigrationCommandOnActiveWorkspaces( _passedParam: string[], options: RunWorkspaceMigrationsOptions, workspaceIds: string[], @@ -133,15 +134,6 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner ); } - @Option({ - flags: '-d, --dry-run', - description: 'Dry run without applying changes', - required: false, - }) - dryRun(): boolean { - return true; - } - @Option({ flags: '-f, --force', description: 'Force migration', diff --git a/packages/twenty-shared/src/types/IsExactly.ts b/packages/twenty-shared/src/types/IsExactly.ts new file mode 100644 index 000000000..6bde5e83a --- /dev/null +++ b/packages/twenty-shared/src/types/IsExactly.ts @@ -0,0 +1,5 @@ +export type IsExactly = [T] extends [U] + ? [U] extends [T] + ? true + : false + : false; diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts index 92bece5db..f29f71571 100644 --- a/packages/twenty-shared/src/types/index.ts +++ b/packages/twenty-shared/src/types/index.ts @@ -1,2 +1,4 @@ export * from './ConnectedAccountProvider'; export * from './FieldMetadataType'; +export * from './IsExactly'; +