Cleaning UpgradeCommand code flow (#19241)

# Introduction
Currently preparing the `UpgradeCommand` refactor, in this way started
by cleaning up the existing avoiding unecessary dependencies to others
services allowing easier readability and concern centralization for
upcoming refactor
The UpgradeCommand was extending up to five classes, overriding
abstracted class and so on. It was also cascade
injecting 3 services

Introducing the `WorkspaceIteratorService` that centralize the commands
set to run over a single workspace logic shared between both atomic
upgrade command call and global upgradeCommand

## Tradeoff
Duplicated `@Option` between both `UpgradeCommandRunner` and
`WorkspaceMigrationRunner`


## Before
```
UpgradeCommand
  └─ extends UpgradeCommandRunner
       └─ extends ActiveOrSuspendedWorkspacesMigrationCommandRunner
            └─ extends WorkspacesMigrationCommandRunner  (owns workspace iteration loop + ORM deps)
                 └─ extends MigrationCommandRunner       (dry-run, verbose, error handling)
                      └─ extends CommandRunner            (nest-commander)
```

## Now

```
UpgradeCommand
  └─ extends UpgradeCommandRunner
       └─ extends CommandRunner                 (nest-commander)
       uses ─► Services         (via composition)
```

## Logging management
At the moment all services are logging, in the best of the world only
the runners should be doing so
This commit is contained in:
Paul Rastoin
2026-04-02 15:24:55 +02:00
committed by GitHub
parent 6ae5900ac9
commit cd23a2bc80
43 changed files with 816 additions and 814 deletions
@@ -11,14 +11,13 @@ import {
UpgradeCommandRunner,
type AllCommands,
} from 'src/database/commands/command-runners/upgrade.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
import { UPGRADE_COMMAND_SUPPORTED_VERSIONS } from 'src/engine/constants/upgrade-command-supported-versions.constant';
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
import { type ConfigVariables } from 'src/engine/core-modules/twenty-config/config-variables';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
const CURRENT_VERSION =
@@ -66,41 +65,31 @@ const buildUpgradeCommandModule = async ({
appVersion,
commandRunner,
}: BuildUpgradeCommandModuleArgs) => {
const mockDataSourceService = {
getLastDataSourceMetadataFromWorkspaceId: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: commandRunner,
useFactory: (
workspaceRepository: Repository<WorkspaceEntity>,
twentyConfigService: TwentyConfigService,
globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
dataSourceService: DataSourceService,
coreEngineVersionService: CoreEngineVersionService,
workspaceVersionService: WorkspaceVersionService,
coreMigrationRunnerService: CoreMigrationRunnerService,
workspaceIteratorService: WorkspaceIteratorService,
) => {
return new commandRunner(
workspaceRepository,
twentyConfigService,
globalWorkspaceOrmManager,
dataSourceService,
coreEngineVersionService,
workspaceVersionService,
coreMigrationRunnerService,
workspaceIteratorService,
);
},
inject: [
getRepositoryToken(WorkspaceEntity),
TwentyConfigService,
GlobalWorkspaceOrmManager,
DataSourceService,
CoreEngineVersionService,
WorkspaceVersionService,
CoreMigrationRunnerService,
WorkspaceIteratorService,
],
},
{
@@ -131,27 +120,43 @@ const buildUpgradeCommandModule = async ({
}),
},
},
{
provide: GlobalWorkspaceOrmManager,
useValue: {
connect: jest.fn(),
destroyDataSourceForWorkspace: jest.fn(),
getDataSourceForWorkspace: jest.fn(),
executeInWorkspaceContext: jest
.fn()
.mockImplementation((fn: () => any, _authContext?: any) => fn()),
},
},
{
provide: DataSourceService,
useValue: mockDataSourceService,
},
CoreEngineVersionService,
WorkspaceVersionService,
{
provide: CoreMigrationRunnerService,
useValue: { run: jest.fn().mockResolvedValue(undefined) },
},
{
provide: WorkspaceIteratorService,
useValue: {
iterate: jest.fn().mockImplementation(async (args: any) => {
const { callback, ...options } = args;
const workspaceIds =
options.workspaceIds ??
workspaces.map((workspace) => workspace.id);
const report = {
fail: [] as any[],
success: [] as any[],
};
for (const [index, workspaceId] of workspaceIds.entries()) {
try {
await callback({
workspaceId,
index,
total: workspaceIds.length,
});
report.success.push({ workspaceId });
} catch (error) {
report.fail.push({ error, workspaceId });
}
}
return report;
}),
},
},
],
}).compile();
@@ -161,7 +166,6 @@ const buildUpgradeCommandModule = async ({
describe('UpgradeCommandRunner', () => {
let upgradeCommandRunner: BasicUpgradeCommandRunner;
let workspaceRepository: Repository<WorkspaceEntity>;
let coreMigrationRunnerService: CoreMigrationRunnerService;
type BuildModuleAndSetupSpiesArgs = {
numberOfWorkspace?: number;
@@ -197,10 +201,6 @@ describe('UpgradeCommandRunner', () => {
jest.spyOn(upgradeCommandRunner['logger'], 'error').mockImplementation();
jest.spyOn(upgradeCommandRunner['logger'], 'warn').mockImplementation();
jest.spyOn(upgradeCommandRunner, 'runOnWorkspace');
coreMigrationRunnerService = module.get(CoreMigrationRunnerService);
workspaceRepository = module.get<Repository<WorkspaceEntity>>(
getRepositoryToken(WorkspaceEntity),
);
@@ -223,16 +223,6 @@ describe('UpgradeCommandRunner', () => {
// @ts-expect-error legacy noImplicitAny
await upgradeCommandRunner.run(passedParams, options);
const { fail: failReport, success: successReport } =
upgradeCommandRunner.migrationReport;
expect(successReport.length).toBe(1);
expect(failReport.length).toBe(0);
[upgradeCommandRunner.runOnWorkspace].forEach((fn) =>
expect(fn).toHaveBeenCalledTimes(1),
);
[workspaceRepository.update].forEach((fn) =>
expect(fn).not.toHaveBeenCalled(),
);
@@ -251,16 +241,12 @@ describe('UpgradeCommandRunner', () => {
// @ts-expect-error legacy noImplicitAny
await upgradeCommandRunner.run(passedParams, options);
[upgradeCommandRunner.runOnWorkspace].forEach((fn) =>
expect(fn).toHaveBeenCalledTimes(numberOfWorkspace),
);
expect(workspaceRepository.update).toHaveBeenNthCalledWith(
numberOfWorkspace,
{ id: expect.any(String) },
{ version: CURRENT_VERSION },
);
expect(upgradeCommandRunner.migrationReport.success.length).toBe(42);
expect(upgradeCommandRunner.migrationReport.fail.length).toBe(0);
expect(workspaceRepository.update).toHaveBeenCalledTimes(numberOfWorkspace);
});
describe('Workspace upgrade should succeed ', () => {
@@ -315,15 +301,10 @@ describe('UpgradeCommandRunner', () => {
// @ts-expect-error legacy noImplicitAny
await upgradeCommandRunner.run(passedParams, options);
const { fail: failReport, success: successReport } =
upgradeCommandRunner.migrationReport;
expect(failReport.length).toBe(0);
expect(successReport.length).toBe(1);
expect(coreMigrationRunnerService.run).toHaveBeenCalledTimes(1);
const { workspaceId } = successReport[0];
expect(workspaceId).toBe('workspace_0');
expect(workspaceRepository.update).toHaveBeenCalledWith(
{ id: 'workspace_0' },
{ version: expect.any(String) },
);
},
);
});
@@ -331,10 +312,7 @@ describe('UpgradeCommandRunner', () => {
describe('Workspace upgrade should fail', () => {
const failingTestUseCases: EachTestingContext<{
input: Omit<BuildModuleAndSetupSpiesArgs, 'numberOfWorkspace'>;
output?: {
failReportWorkspaceId: string;
expectedErrorMessage: string;
};
expectedErrorMessage: string;
}>[] = [
{
title: 'when workspace version is not equal to fromVersion',
@@ -344,10 +322,8 @@ describe('UpgradeCommandRunner', () => {
version: '0.1.0',
},
},
output: {
failReportWorkspaceId: 'workspace_0',
expectedErrorMessage: `Unable to run the upgrade command. Aborting the upgrade process.\nPlease ensure that all workspaces are on at least the previous minor version (${PREVIOUS_VERSION}).\nIf any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.`,
},
expectedErrorMessage:
'Unable to run the upgrade command. Aborting the upgrade process.',
},
},
{
@@ -358,10 +334,8 @@ describe('UpgradeCommandRunner', () => {
version: null,
},
},
output: {
failReportWorkspaceId: 'workspace_0',
expectedErrorMessage: `Unable to run the upgrade command. Aborting the upgrade process.\nPlease ensure that all workspaces are on at least the previous minor version (${PREVIOUS_VERSION}).\nIf any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.`,
},
expectedErrorMessage:
'Unable to run the upgrade command. Aborting the upgrade process.',
},
},
{
@@ -370,11 +344,8 @@ describe('UpgradeCommandRunner', () => {
input: {
appVersion: null,
},
output: {
failReportWorkspaceId: 'global',
expectedErrorMessage:
'APP_VERSION is not defined, please double check your env variables',
},
expectedErrorMessage:
'APP_VERSION is not defined, please double check your env variables',
},
},
{
@@ -383,11 +354,8 @@ describe('UpgradeCommandRunner', () => {
input: {
appVersion: '42.0.0',
},
output: {
failReportWorkspaceId: 'global',
expectedErrorMessage:
'No command found for version 42.0.0. Please check the commands record.',
},
expectedErrorMessage:
'No command found for version 42.0.0. Please check the commands record.',
},
},
{
@@ -396,33 +364,22 @@ describe('UpgradeCommandRunner', () => {
input: {
appVersion: UPGRADE_COMMAND_SUPPORTED_VERSIONS[0],
},
output: {
failReportWorkspaceId: 'global',
expectedErrorMessage: `No previous version found for version ${UPGRADE_COMMAND_SUPPORTED_VERSIONS[0]}. Available versions: ${UPGRADE_COMMAND_SUPPORTED_VERSIONS.join(', ')}`,
},
expectedErrorMessage: `No previous version found for version ${UPGRADE_COMMAND_SUPPORTED_VERSIONS[0]}`,
},
},
];
it.each(eachTestingContextFilter(failingTestUseCases))(
'$title',
async ({ context: { input, output } }) => {
async ({ context: { input, expectedErrorMessage } }) => {
await buildModuleAndSetupSpies(input);
const passedParams: string[] = [];
const options = {};
await upgradeCommandRunner.run(passedParams, options);
const { fail: failReport, success: successReport } =
upgradeCommandRunner.migrationReport;
expect(successReport.length).toBe(0);
expect(failReport.length).toBe(1);
const { workspaceId, error } = failReport[0];
expect(workspaceId).toBe(output?.failReportWorkspaceId ?? 'global');
expect(error).toEqual(new Error(output?.expectedErrorMessage ?? ''));
await expect(
upgradeCommandRunner.run(passedParams, options),
).rejects.toThrow(expectedErrorMessage);
},
);
});
@@ -0,0 +1,23 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import {
WorkspaceCommandRunner,
type WorkspaceCommandOptions,
} from 'src/database/commands/command-runners/workspace.command-runner';
import { type WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
export type ActiveOrSuspendedWorkspaceCommandOptions = WorkspaceCommandOptions;
export abstract class ActiveOrSuspendedWorkspaceCommandRunner<
Options extends
ActiveOrSuspendedWorkspaceCommandOptions = ActiveOrSuspendedWorkspaceCommandOptions,
> extends WorkspaceCommandRunner<Options> {
constructor(
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceIteratorService, [
WorkspaceActivationStatus.ACTIVE,
WorkspaceActivationStatus.SUSPENDED,
]);
}
}
@@ -1,29 +0,0 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { type Repository } from 'typeorm';
import {
WorkspacesMigrationCommandRunner,
type WorkspacesMigrationCommandOptions,
} from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { type DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { type GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
export type ActiveOrSuspendedWorkspacesMigrationCommandOptions =
WorkspacesMigrationCommandOptions;
export abstract class ActiveOrSuspendedWorkspacesMigrationCommandRunner<
Options extends
ActiveOrSuspendedWorkspacesMigrationCommandOptions = ActiveOrSuspendedWorkspacesMigrationCommandOptions,
> extends WorkspacesMigrationCommandRunner<Options> {
constructor(
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService, [
WorkspaceActivationStatus.ACTIVE,
WorkspaceActivationStatus.SUSPENDED,
]);
}
}
@@ -52,11 +52,11 @@ export abstract class MigrationCommandRunner extends CommandRunner {
try {
await this.runMigrationCommand(passedParams, options);
this.logger.log(chalk.blue('Command completed!'));
} catch (error) {
this.logger.error(chalk.red(`Command failed`));
throw error;
} finally {
this.logger.log(chalk.blue('Command completed!'));
}
}
@@ -1,104 +1,143 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { CommandRunner, Option } from 'nest-commander';
import { SemVer } from 'semver';
import { isDefined } from 'twenty-shared/utils';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandOptions,
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
type RunOnWorkspaceArgs,
WorkspaceCommandRunner,
} from 'src/database/commands/command-runners/workspace.command-runner';
import {
RunOnWorkspaceArgs,
WorkspacesMigrationCommandRunner,
} from 'src/database/commands/command-runners/workspaces-migration.command-runner';
type WorkspaceIteratorContext,
WorkspaceIteratorService,
} from 'src/database/commands/command-runners/workspace-iterator.service';
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
import { CommandLogger } from 'src/database/commands/logger';
import { type UpgradeCommandVersion } from 'src/engine/constants/upgrade-command-supported-versions.constant';
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { type DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
import {
type CompareVersionMajorAndMinorReturnType,
compareVersionMajorAndMinor,
} from 'src/utils/version/compare-version-minor-and-major';
export type VersionCommands = (
| WorkspacesMigrationCommandRunner
| ActiveOrSuspendedWorkspacesMigrationCommandRunner
)[];
export type VersionCommands = WorkspaceCommandRunner[];
export type AllCommands = Record<UpgradeCommandVersion, VersionCommands>;
export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
private fromWorkspaceVersion: SemVer;
private currentAppVersion: SemVer;
export type UpgradeCommandOptions = {
workspaceId?: Set<string>;
startFromWorkspaceId?: string;
workspaceCountLimit?: number;
dryRun?: boolean;
verbose?: boolean;
};
type VersionContext = {
fromWorkspaceVersion: SemVer;
currentAppVersion: SemVer;
commands: VersionCommands;
};
export abstract class UpgradeCommandRunner extends CommandRunner {
protected logger: CommandLogger;
public abstract allCommands: AllCommands;
public commands: VersionCommands;
public readonly VALIDATE_WORKSPACE_VERSION_FEATURE_FLAG?: true;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyConfigService: TwentyConfigService,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly coreEngineVersionService: CoreEngineVersionService,
protected readonly workspaceVersionService: WorkspaceVersionService,
protected readonly coreMigrationRunnerService: CoreMigrationRunnerService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super();
this.logger = new CommandLogger({
verbose: false,
constructorName: this.constructor.name,
});
}
private setUpgradeContextVersionsAndCommandsForCurrentAppVersion() {
const upgradeContextIsAlreadyDefined = [
this.currentAppVersion,
this.commands,
this.fromWorkspaceVersion,
].every(isDefined);
if (upgradeContextIsAlreadyDefined) {
return;
}
const currentAppVersion = this.coreEngineVersionService.getCurrentVersion();
const currentVersionMajorMinor =
`${currentAppVersion.major}.${currentAppVersion.minor}.0` as UpgradeCommandVersion;
const currentCommands = this.allCommands[currentVersionMajorMinor];
if (!isDefined(currentCommands)) {
throw new Error(
`No command found for version ${currentAppVersion}. Please check the commands record.`,
);
}
const previousVersion = this.coreEngineVersionService.getPreviousVersion();
this.commands = currentCommands;
this.fromWorkspaceVersion = previousVersion;
this.currentAppVersion = currentAppVersion;
const message = [
'Initialized upgrade context with:',
`- currentVersion (migrating to): ${currentAppVersion}`,
`- fromWorkspaceVersion: ${previousVersion}`,
`- ${this.commands.length} commands`,
];
this.logger.log(chalk.blue(message.join('\n ')));
@Option({
flags: '-d, --dry-run',
description: 'Simulate the command without making actual changes',
required: false,
})
parseDryRun(): boolean {
return true;
}
override async runMigrationCommand(
passedParams: string[],
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions,
@Option({
flags: '-v, --verbose',
description: 'Verbose output',
required: false,
})
parseVerbose(): boolean {
return true;
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description:
'workspace id. Command runs on all active/suspended workspaces if not provided.',
required: false,
})
parseWorkspaceId(val: string, previous?: Set<string>): Set<string> {
const accumulator = previous ?? new Set<string>();
accumulator.add(val);
return accumulator;
}
@Option({
flags: '--start-from-workspace-id [workspace_id]',
description:
'Start from a specific workspace id. Workspaces are processed in ascending order of id.',
required: false,
})
parseStartFromWorkspaceId(val: string): string {
return val;
}
@Option({
flags: '--workspace-count-limit [count]',
description:
'Limit the number of workspaces to process. Workspaces are processed in ascending order of id.',
required: false,
})
parseWorkspaceCountLimit(val: string): number {
const limit = parseInt(val);
if (isNaN(limit)) {
throw new Error('Workspace count limit must be a number');
}
if (limit <= 0) {
throw new Error('Workspace count limit must be greater than 0');
}
return limit;
}
override async run(
_passedParams: string[],
options: UpgradeCommandOptions,
): Promise<void> {
try {
this.setUpgradeContextVersionsAndCommandsForCurrentAppVersion();
if (options.verbose) {
this.logger = new CommandLogger({
verbose: true,
constructorName: this.constructor.name,
});
}
try {
const versionContext = this.resolveVersionContext();
// On fresh installs there are no workspaces yet, so skip the
// per-workspace upgrade loop (core migrations already ran above).
const hasWorkspaces =
await this.workspaceVersionService.hasActiveOrSuspendedWorkspaces();
@@ -110,75 +149,129 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
return;
}
const workspacesThatAreBelowFromWorkspaceVersion =
const workspacesBelowMinimumVersion =
await this.workspaceVersionService.getWorkspacesBelowVersion(
this.fromWorkspaceVersion.version,
versionContext.fromWorkspaceVersion.version,
);
if (workspacesThatAreBelowFromWorkspaceVersion.length > 0) {
this.migrationReport.fail.push(
...workspacesThatAreBelowFromWorkspaceVersion.map((workspace) => ({
error: new Error(
`Unable to run the upgrade command. Aborting the upgrade process.
Please ensure that all workspaces are on at least the previous minor version (${this.fromWorkspaceVersion.version}).
If any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.`,
),
workspaceId: workspace.id,
})),
if (workspacesBelowMinimumVersion.length > 0) {
const ineligibleIds = workspacesBelowMinimumVersion
.map((workspace) => workspace.id)
.join(', ');
throw new Error(
`Unable to run the upgrade command. Aborting the upgrade process.
Workspaces below minimum version (${versionContext.fromWorkspaceVersion.version}): ${ineligibleIds}.
Please roll back to that version and run the upgrade command again.`,
);
}
} catch (error) {
this.migrationReport.fail.push({
error,
workspaceId: 'global',
});
}
if (this.migrationReport.fail.length > 0) {
this.migrationReport.fail.forEach(({ error, workspaceId }) =>
await this.coreMigrationRunnerService.run();
const iteratorReport = await this.workspaceIteratorService.iterate({
workspaceIds:
options.workspaceId && options.workspaceId.size > 0
? Array.from(options.workspaceId)
: undefined,
startFromWorkspaceId: options.startFromWorkspaceId,
workspaceCountLimit: options.workspaceCountLimit,
dryRun: options.dryRun,
callback: async (context) => {
await this.runOnWorkspace(context, options, versionContext);
},
});
if (iteratorReport.fail.length > 0) {
this.logger.error(
`Error in workspace ${workspaceId}: ${error.message}`,
chalk.red(
`Upgrade completed with ${iteratorReport.fail.length} workspace failure(s)`,
),
);
}
this.logger.log(
chalk.blue(
`Upgrade summary: ${iteratorReport.success.length} succeeded, ${iteratorReport.fail.length} failed`,
),
);
return;
this.logger.log(chalk.blue('Command completed!'));
} catch (error) {
this.logger.error(chalk.red(`Upgrade failed: ${error.message}`));
throw error;
}
await this.coreMigrationRunnerService.run();
await super.runMigrationCommand(passedParams, options);
}
override async runOnWorkspace(args: RunOnWorkspaceArgs): Promise<void> {
this.setUpgradeContextVersionsAndCommandsForCurrentAppVersion();
private resolveVersionContext(): VersionContext {
const currentAppVersion = this.coreEngineVersionService.getCurrentVersion();
const currentVersionMajorMinor =
`${currentAppVersion.major}.${currentAppVersion.minor}.0` as UpgradeCommandVersion;
const commands = this.allCommands[currentVersionMajorMinor];
const { workspaceId, index, total, options } = args;
if (!isDefined(commands)) {
throw new Error(
`No command found for version ${currentAppVersion}. Please check the commands record.`,
);
}
const fromWorkspaceVersion =
this.coreEngineVersionService.getPreviousVersion();
this.logger.log(
chalk.blue(
`${options.dryRun ? '(dry run) ' : ''}Upgrading workspace ${workspaceId} from=${this.fromWorkspaceVersion} to=${this.currentAppVersion} ${index + 1}/${total}`,
[
'Initialized upgrade context with:',
`- currentVersion (migrating to): ${currentAppVersion}`,
`- fromWorkspaceVersion: ${fromWorkspaceVersion}`,
`- ${commands.length} commands`,
].join('\n '),
),
);
const workspaceVersionCompareResult =
await this.retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion(
return { fromWorkspaceVersion, currentAppVersion, commands };
}
private async runOnWorkspace(
iteratorContext: WorkspaceIteratorContext,
options: UpgradeCommandOptions,
versionContext: VersionContext,
): Promise<void> {
const { workspaceId, index, total } = iteratorContext;
const { fromWorkspaceVersion, currentAppVersion, commands } =
versionContext;
this.logger.log(
chalk.blue(
`${options.dryRun ? '(dry run) ' : ''}Upgrading workspace ${workspaceId} from=${fromWorkspaceVersion} to=${currentAppVersion} ${index + 1}/${total}`,
),
);
const versionCompareResult =
await this.compareWorkspaceVersionToFromVersion(
workspaceId,
fromWorkspaceVersion,
);
switch (workspaceVersionCompareResult) {
switch (versionCompareResult) {
case 'lower': {
throw new Error(
`WORKSPACE_VERSION_MISSMATCH Upgrade for workspace ${workspaceId} failed as its version is beneath fromWorkspaceVersion=${this.fromWorkspaceVersion.version}`,
`WORKSPACE_VERSION_MISSMATCH Upgrade for workspace ${workspaceId} failed as its version is beneath fromWorkspaceVersion=${fromWorkspaceVersion.version}`,
);
}
case 'equal': {
for (const command of this.commands) {
await command.runOnWorkspace(args);
for (const command of commands) {
await command.runOnWorkspace({
options: options as RunOnWorkspaceArgs['options'],
workspaceId,
dataSource: iteratorContext.dataSource,
index,
total,
});
}
if (!options.dryRun) {
await this.workspaceRepository.update(
{ id: workspaceId },
{ version: this.currentAppVersion.version },
{ version: currentAppVersion.version },
);
}
@@ -198,15 +291,14 @@ If any workspaces are not on the previous minor version, roll back to that versi
return;
}
default: {
throw new Error(
`Should never occur, encountered unexpected value from retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion ${workspaceVersionCompareResult}`,
);
assertUnreachable(versionCompareResult);
}
}
}
private async retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion(
private async compareWorkspaceVersionToFromVersion(
workspaceId: string,
fromWorkspaceVersion: SemVer,
): Promise<CompareVersionMajorAndMinorReturnType> {
const workspace = await this.workspaceRepository.findOneByOrFail({
id: workspaceId,
@@ -219,7 +311,7 @@ If any workspaces are not on the previous minor version, roll back to that versi
return compareVersionMajorAndMinor(
currentWorkspaceVersion,
this.fromWorkspaceVersion.version,
fromWorkspaceVersion.version,
);
}
}
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
@Module({
imports: [TypeOrmModule.forFeature([WorkspaceEntity]), DataSourceModule],
providers: [WorkspaceIteratorService],
exports: [WorkspaceIteratorService],
})
export class WorkspaceIteratorModule {}
@@ -0,0 +1,139 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { In, MoreThanOrEqual, Repository } from 'typeorm';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
export type WorkspaceIteratorArgs = {
workspaceIds?: string[];
activationStatuses?: WorkspaceActivationStatus[];
startFromWorkspaceId?: string;
workspaceCountLimit?: number;
dryRun?: boolean;
callback: (context: WorkspaceIteratorContext) => Promise<void>;
};
export type WorkspaceIteratorContext = {
workspaceId: string;
dataSource?: GlobalWorkspaceDataSource;
index: number;
total: number;
};
export type WorkspaceIteratorReport = {
fail: {
workspaceId: string;
error: Error;
}[];
success: {
workspaceId: string;
}[];
};
const DEFAULT_ACTIVATION_STATUSES = [
WorkspaceActivationStatus.ACTIVE,
WorkspaceActivationStatus.SUSPENDED,
];
@Injectable()
export class WorkspaceIteratorService {
private readonly logger = new Logger(WorkspaceIteratorService.name);
constructor(
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly dataSourceService: DataSourceService,
) {}
async iterate(args: WorkspaceIteratorArgs): Promise<WorkspaceIteratorReport> {
const { callback, ...options } = args;
const report: WorkspaceIteratorReport = {
fail: [],
success: [],
};
const workspaceIdsToProcess =
options.workspaceIds && options.workspaceIds.length > 0
? options.workspaceIds
: await this.fetchWorkspaceIds(options);
if (options.dryRun) {
this.logger.log(chalk.yellow('Dry run mode: No changes will be applied'));
}
for (const [index, workspaceId] of workspaceIdsToProcess.entries()) {
this.logger.log(
`Running on workspace ${workspaceId} ${index + 1}/${workspaceIdsToProcess.length}`,
);
try {
const authContext = buildSystemAuthContext(workspaceId);
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
async () => {
const workspaceHasDataSource =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId(
workspaceId,
);
const dataSource = isDefined(workspaceHasDataSource)
? await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource()
: undefined;
await callback({
workspaceId,
dataSource,
index,
total: workspaceIdsToProcess.length,
});
},
authContext,
);
report.success.push({ workspaceId });
} catch (error: unknown) {
report.fail.push({ error: error as Error, workspaceId });
}
}
report.fail.forEach(({ error, workspaceId }) =>
this.logger.error(
`Error in workspace ${workspaceId}: ${error.message}`,
error.stack,
),
);
return report;
}
private async fetchWorkspaceIds(
options: Omit<WorkspaceIteratorArgs, 'callback'>,
): Promise<string[]> {
const activationStatuses =
options.activationStatuses ?? DEFAULT_ACTIVATION_STATUSES;
const workspaces = await this.workspaceRepository.find({
select: ['id'],
where: {
activationStatus: In(activationStatuses),
...(options.startFromWorkspaceId
? { id: MoreThanOrEqual(options.startFromWorkspaceId) }
: {}),
},
order: { id: 'ASC' },
take: options.workspaceCountLimit,
});
return workspaces.map((workspace) => workspace.id);
}
}
@@ -0,0 +1,140 @@
import chalk from 'chalk';
import { CommandRunner, Option } from 'nest-commander';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { CommandLogger } from 'src/database/commands/logger';
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
export type WorkspaceCommandOptions = {
workspaceId?: Set<string>;
startFromWorkspaceId?: string;
workspaceCountLimit?: number;
dryRun?: boolean;
verbose?: boolean;
};
export type RunOnWorkspaceArgs = {
options: WorkspaceCommandOptions;
workspaceId: string;
dataSource?: GlobalWorkspaceDataSource;
index: number;
total: number;
};
export abstract class WorkspaceCommandRunner<
Options extends WorkspaceCommandOptions = WorkspaceCommandOptions,
> extends CommandRunner {
protected logger: CommandLogger;
constructor(
protected readonly workspaceIteratorService: WorkspaceIteratorService,
protected readonly activationStatuses: WorkspaceActivationStatus[],
) {
super();
this.logger = new CommandLogger({
verbose: false,
constructorName: this.constructor.name,
});
}
@Option({
flags: '-d, --dry-run',
description: 'Simulate the command without making actual changes',
required: false,
})
parseDryRun(): boolean {
return true;
}
@Option({
flags: '-v, --verbose',
description: 'Verbose output',
required: false,
})
parseVerbose(): boolean {
return true;
}
@Option({
flags: '--start-from-workspace-id [workspace_id]',
description:
'Start from a specific workspace id. Workspaces are processed in ascending order of id.',
required: false,
})
parseStartFromWorkspaceId(val: string): string {
return val;
}
@Option({
flags: '--workspace-count-limit [count]',
description:
'Limit the number of workspaces to process. Workspaces are processed in ascending order of id.',
required: false,
})
parseWorkspaceCountLimit(val: string): number {
const limit = parseInt(val);
if (isNaN(limit)) {
throw new Error('Workspace count limit must be a number');
}
if (limit <= 0) {
throw new Error('Workspace count limit must be greater than 0');
}
return limit;
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description:
'workspace id. Command runs on all workspaces matching the activation statuses if not provided.',
required: false,
})
parseWorkspaceId(val: string, previous?: Set<string>): Set<string> {
const accumulator = previous ?? new Set<string>();
accumulator.add(val);
return accumulator;
}
override async run(_passedParams: string[], options: Options): Promise<void> {
if (options.verbose) {
this.logger = new CommandLogger({
verbose: true,
constructorName: this.constructor.name,
});
}
try {
await this.workspaceIteratorService.iterate({
workspaceIds:
options.workspaceId && options.workspaceId.size > 0
? Array.from(options.workspaceId)
: undefined,
activationStatuses: this.activationStatuses,
startFromWorkspaceId: options.startFromWorkspaceId,
workspaceCountLimit: options.workspaceCountLimit,
dryRun: options.dryRun,
callback: async (context) => {
await this.runOnWorkspace({
options,
workspaceId: context.workspaceId,
dataSource: context.dataSource,
index: context.index,
total: context.total,
});
},
});
this.logger.log(chalk.blue('Command completed!'));
} catch (error) {
this.logger.error(chalk.red(`Command failed`));
throw error;
}
}
public abstract runOnWorkspace(args: RunOnWorkspaceArgs): Promise<void>;
}
@@ -1,189 +0,0 @@
import chalk from 'chalk';
import { Option } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { In, MoreThanOrEqual, type Repository } from 'typeorm';
import { MigrationCommandRunner } from 'src/database/commands/command-runners/migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { type DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
import { type GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
export type WorkspacesMigrationCommandOptions = {
workspaceIds: string[];
startFromWorkspaceId?: string;
workspaceCountLimit?: number;
dryRun?: boolean;
verbose?: boolean;
};
export type RunOnWorkspaceArgs = {
options: WorkspacesMigrationCommandOptions;
workspaceId: string;
dataSource?: GlobalWorkspaceDataSource;
index: number;
total: number;
};
export type WorkspaceMigrationReport = {
fail: {
workspaceId: string;
error: Error;
}[];
success: {
workspaceId: string;
}[];
};
export abstract class WorkspacesMigrationCommandRunner<
Options extends
WorkspacesMigrationCommandOptions = WorkspacesMigrationCommandOptions,
> extends MigrationCommandRunner {
protected workspaceIds: Set<string> = new Set();
private startFromWorkspaceId: string | undefined;
private workspaceCountLimit: number | undefined;
public migrationReport: WorkspaceMigrationReport = {
fail: [],
success: [],
};
constructor(
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly activationStatuses: WorkspaceActivationStatus[],
) {
super();
}
@Option({
flags: '--start-from-workspace-id [workspace_id]',
description:
'Start from a specific workspace id. Workspaces are processed in ascending order of id.',
required: false,
})
parseStartFromWorkspaceId(val: string): string {
this.startFromWorkspaceId = val;
return val;
}
@Option({
flags: '--workspace-count-limit [count]',
description:
'Limit the number of workspaces to process. Workspaces are processed in ascending order of id.',
required: false,
})
parseWorkspaceCountLimit(val: string): number {
this.workspaceCountLimit = parseInt(val);
if (isNaN(this.workspaceCountLimit)) {
throw new Error('Workspace count limit must be a number');
}
if (this.workspaceCountLimit <= 0) {
throw new Error('Workspace count limit must be greater than 0');
}
return this.workspaceCountLimit;
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description:
'workspace id. Command runs on all workspaces matching the activation statuses if not provided.',
required: false,
})
parseWorkspaceId(val: string): Set<string> {
this.workspaceIds.add(val);
return this.workspaceIds;
}
protected async fetchWorkspaceIds(): Promise<string[]> {
const workspaces = await this.workspaceRepository.find({
select: ['id'],
where: {
activationStatus: In(this.activationStatuses),
...(this.startFromWorkspaceId
? { id: MoreThanOrEqual(this.startFromWorkspaceId) }
: {}),
},
order: {
id: 'ASC',
},
take: this.workspaceCountLimit,
});
return workspaces.map((workspace) => workspace.id);
}
override async runMigrationCommand(
_passedParams: string[],
options: Options,
) {
const workspaceIdsToProcess =
this.workspaceIds.size > 0
? Array.from(this.workspaceIds)
: await this.fetchWorkspaceIds();
if (options.dryRun) {
this.logger.log(chalk.yellow('Dry run mode: No changes will be applied'));
}
for (const [index, workspaceId] of workspaceIdsToProcess.entries()) {
this.logger.log(
`Upgrading workspace ${workspaceId} ${index + 1}/${workspaceIdsToProcess.length}`,
);
try {
const authContext = buildSystemAuthContext(workspaceId);
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
async () => {
const workspaceHasDataSource =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId(
workspaceId,
);
const dataSource = isDefined(workspaceHasDataSource)
? await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource()
: undefined;
await this.runOnWorkspace({
options,
workspaceId,
dataSource,
index: index,
total: workspaceIdsToProcess.length,
});
},
authContext,
);
this.migrationReport.success.push({
workspaceId,
});
} catch (error) {
this.migrationReport.fail.push({
error,
workspaceId,
});
this.logger.warn(
chalk.red(`Error in workspace ${workspaceId}: ${error.message}`),
);
}
}
this.migrationReport.fail.forEach(({ error, workspaceId }) =>
this.logger.error(
`Error in workspace ${workspaceId}: ${error.message}`,
error.stack,
),
);
}
public abstract runOnWorkspace(args: RunOnWorkspaceArgs): Promise<void>;
}
@@ -1,21 +1,17 @@
import { InjectRepository } from '@nestjs/typeorm';
import { isNonEmptyString } from '@sniptt/guards';
import { Command } from 'nest-commander';
import { FeatureFlagKey } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { v4 as uuidv4 } from 'uuid';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
@@ -38,19 +34,17 @@ import {
description:
'Backfill missing standard and trigger workflow version command menu items for existing workspaces and enable IS_COMMAND_MENU_ITEM_ENABLED feature flag',
})
export class BackfillCommandMenuItemsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BackfillCommandMenuItemsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
private readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
private readonly applicationService: ApplicationService,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly featureFlagService: FeatureFlagService,
private readonly workspaceCacheService: WorkspaceCacheService,
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,32 +1,27 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { makeNavigationMenuItemTypeNotNullQueries } from 'src/database/typeorm/core/migrations/utils/1773681736596-makeNavigationMenuItemTypeNotNull.util';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:backfill-navigation-menu-item-type',
description:
'Backfill navigation menu item type based on existing columns, then apply NOT NULL and CHECK constraints',
})
export class BackfillNavigationMenuItemTypeCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BackfillNavigationMenuItemTypeCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,31 +1,26 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
@Command({
name: 'upgrade:1-20:backfill-select-field-option-ids',
description:
'Backfill missing ids on SELECT and MULTI_SELECT field metadata options',
})
export class BackfillSelectFieldOptionIdsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BackfillSelectFieldOptionIdsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,4 +1,3 @@
import { Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
@@ -6,33 +5,24 @@ import { In, Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
@Command({
name: 'upgrade:1-20:delete-orphan-navigation-menu-items',
description: 'Delete navigation menu items pointing to deleted views',
})
export class DeleteOrphanNavigationMenuItemsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
protected readonly logger = new Logger(
DeleteOrphanNavigationMenuItemsCommand.name,
);
export class DeleteOrphanNavigationMenuItemsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectRepository(NavigationMenuItemEntity)
private readonly navigationMenuItemRepository: Repository<NavigationMenuItemEntity>,
private readonly workspaceCacheService: WorkspaceCacheService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,35 +1,30 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, IsNull, type Repository } from 'typeorm';
import { DataSource, IsNull } from 'typeorm';
import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:identify-field-permission-metadata',
description:
'Identify field permission metadata (backfill universalIdentifier and applicationId)',
})
export class IdentifyFieldPermissionMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class IdentifyFieldPermissionMetadataCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,35 +1,30 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, IsNull, type Repository } from 'typeorm';
import { DataSource, IsNull } from 'typeorm';
import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:identify-object-permission-metadata',
description:
'Identify object permission metadata (backfill universalIdentifier and applicationId)',
})
export class IdentifyObjectPermissionMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class IdentifyObjectPermissionMetadataCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,35 +1,30 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, IsNull, type Repository } from 'typeorm';
import { DataSource, IsNull } from 'typeorm';
import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:identify-permission-flag-metadata',
description:
'Identify permission flag metadata (backfill universalIdentifier and applicationId)',
})
export class IdentifyPermissionFlagMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class IdentifyPermissionFlagMetadataCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,32 +1,27 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, type Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { makeFieldPermissionUniversalIdentifierAndApplicationIdNotNullQueries } from 'src/database/typeorm/core/migrations/utils/1773400000000-make-field-permission-universal-identifier-and-application-id-not-null.util';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:make-field-permission-universal-identifier-and-application-id-not-nullable-migration',
description:
'Set NOT NULL on fieldPermission universalIdentifier and applicationId, add unique index and FK (run identify-field-permission-metadata first)',
})
export class MakeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MakeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,32 +1,27 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, type Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { makeObjectPermissionUniversalIdentifierAndApplicationIdNotNullQueries } from 'src/database/typeorm/core/migrations/utils/1773317160558-make-object-permission-universal-identifier-and-application-id-not-null.util';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:make-object-permission-universal-identifier-and-application-id-not-nullable-migration',
description:
'Set NOT NULL on objectPermission universalIdentifier and applicationId, add unique index and FK (run identify-object-permission-metadata first)',
})
export class MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,32 +1,27 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, type Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { makePermissionFlagUniversalIdentifierAndApplicationIdNotNullQueries } from 'src/database/typeorm/core/migrations/utils/1773232418467-make-permission-flag-universal-identifier-and-application-id-not-null.util';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-20:make-permission-flag-universal-identifier-and-application-id-not-nullable-migration',
description:
'Set NOT NULL on permissionFlag universalIdentifier and applicationId, add unique index and FK (run identify-permission-flag-metadata first)',
})
export class MakePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MakePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,14 +1,12 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
@@ -16,19 +14,16 @@ import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/works
name: 'upgrade:1-20:make-workflow-searchable',
description: 'Set isSearchable to true on the workflow object metadata',
})
export class MakeWorkflowSearchableCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MakeWorkflowSearchableCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
private readonly workspaceCacheService: WorkspaceCacheService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -5,14 +5,13 @@ import { Command } from 'nest-commander';
import { FeatureFlagKey } from 'twenty-shared/types';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity';
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
import { MessageFolderEntity } from 'src/engine/metadata-modules/message-folder/entities/message-folder.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@@ -27,12 +26,9 @@ import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-membe
description:
'Backfill connectedAccount, messageChannel, calendarChannel, and messageFolder to core metadata schema',
})
export class MigrateMessagingInfrastructureToMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MigrateMessagingInfrastructureToMetadataCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
private readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
@InjectRepository(ConnectedAccountEntity)
private readonly connectedAccountRepository: Repository<ConnectedAccountEntity>,
@InjectRepository(MessageChannelEntity)
@@ -44,8 +40,9 @@ export class MigrateMessagingInfrastructureToMetadataCommand extends ActiveOrSus
@InjectRepository(UserWorkspaceEntity)
private readonly userWorkspaceRepository: Repository<UserWorkspaceEntity>,
private readonly featureFlagService: FeatureFlagService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,18 +1,16 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { FeatureFlagKey } from 'twenty-shared/types';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util';
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/workspace-cache-key.type';
@@ -22,20 +20,17 @@ import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/wor
description:
'Migrate deprecated RICH_TEXT (V1) to TEXT and rename RICH_TEXT_V2 to RICH_TEXT. The underlying column type is already text, so only the metadata needs updating.',
})
export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
private readonly featureFlagService: FeatureFlagService,
private readonly workspaceCacheService: WorkspaceCacheService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,31 +1,23 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
@Command({
name: 'upgrade:1-20:seed-cli-application-registration',
description:
'Seed the Twenty CLI application registration for OAuth-based CLI login',
})
export class SeedCliApplicationRegistrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class SeedCliApplicationRegistrationCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRun = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly applicationRegistrationService: ApplicationRegistrationService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,29 +1,24 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
@Command({
name: 'upgrade:1-20:update-standard-index-view-names',
description:
'Update standard index view names to use translatable template placeholders',
})
export class UpdateStandardIndexViewNamesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class UpdateStandardIndexViewNamesCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { BackfillCommandMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-command-menu-items.command';
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command';
import { BackfillSelectFieldOptionIdsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command';
@@ -55,6 +56,7 @@ import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-commo
WorkspaceMigrationModule,
FeatureFlagModule,
WorkflowCommonModule,
WorkspaceIteratorModule,
],
providers: [
IdentifyPermissionFlagMetadataCommand,
@@ -1,21 +1,19 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, type Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { addGlobalKeyValuePairUniqueIndexQueries } from 'src/database/typeorm/core/migrations/utils/1774700000000-add-global-key-value-pair-unique-index.util';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-21:add-global-key-value-pair-unique-index',
description:
'Deduplicate global keyValuePair rows and add the null/null unique index',
})
export class AddGlobalKeyValuePairUniqueIndexCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class AddGlobalKeyValuePairUniqueIndexCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private hasRunOnce = false;
private async deduplicateGlobalKeyValuePairs(
@@ -44,14 +42,11 @@ export class AddGlobalKeyValuePairUniqueIndexCommand extends ActiveOrSuspendedWo
}
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -5,28 +5,26 @@ import { Command } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'upgrade:1-21:backfill-datasource-to-workspace',
description:
'Backfill workspace.databaseSchema from the dataSource entity for workspaces that have not been migrated yet',
})
export class BackfillDatasourceToWorkspaceCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BackfillDatasourceToWorkspaceCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly workspaceRepository: Repository<WorkspaceEntity>,
@InjectRepository(DataSourceEntity)
private readonly dataSourceRepository: Repository<DataSourceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,5 +1,3 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import {
CoreObjectNameSingular,
@@ -9,16 +7,14 @@ import {
ViewType,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { v4 } from 'uuid';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
@@ -31,7 +27,6 @@ import { FieldDisplayMode } from 'src/engine/metadata-modules/page-layout-widget
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import {
GRID_POSITIONS,
@@ -91,18 +86,15 @@ const isRelationTargetAvailable = (
description:
'Backfill RECORD_PAGE page layouts, sync FIELDS_WIDGET view fields, create FIELD widgets, and enable IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
})
export class BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly applicationService: ApplicationService,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly featureFlagService: FeatureFlagService,
private readonly workspaceCacheService: WorkspaceCacheService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,15 +1,10 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant';
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
@@ -38,17 +33,14 @@ const NEW_UNIVERSAL_IDENTIFIERS = new Set([
description:
'Merge single/multiple record engine command menu items into unified commands (delete, restore, destroy, export)',
})
export class DeduplicateEngineCommandsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class DeduplicateEngineCommandsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly applicationService: ApplicationService,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceCacheService: WorkspaceCacheService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,14 +1,10 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
@@ -45,17 +41,15 @@ const TEXT_AGENT_DEFAULT_OUTPUT_SCHEMA = {
description:
'Migrate AI agents with text response format to JSON with a default response field',
})
export class MigrateAiAgentTextToJsonResponseFormatCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MigrateAiAgentTextToJsonResponseFormatCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
private readonly workspaceCacheService: WorkspaceCacheService,
private readonly applicationService: ApplicationService,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,13 +1,11 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
const EDIT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER =
'd9794c67-1799-424f-8871-5ea771dd4a6d';
@@ -16,16 +14,13 @@ const EDIT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER =
name: 'upgrade:1-21:update-edit-layout-command-menu-item-label',
description: 'Update Edit Page Layout command menu item label to Edit Layout',
})
export class UpdateEditLayoutCommandMenuItemLabelCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class UpdateEditLayoutCommandMenuItemLabelCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { AddGlobalKeyValuePairUniqueIndexCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-add-global-key-value-pair-unique-index.command';
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
@@ -23,6 +24,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
ApplicationModule,
WorkspaceMigrationModule,
FeatureFlagModule,
WorkspaceIteratorModule,
],
providers: [
AddGlobalKeyValuePairUniqueIndexCommand,
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { CoreMigrationRunnerModule } from 'src/database/commands/core-migration-runner/core-migration-runner.module';
import { V1_20_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-20/1-20-upgrade-version-command.module';
import { V1_21_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-21/1-21-upgrade-version-command.module';
import { UpgradeCommand } from 'src/database/commands/upgrade-version-command/upgrade.command';
import { CoreEngineVersionModule } from 'src/engine/core-engine-version/core-engine-version.module';
import { CoreMigrationRunnerModule } from 'src/database/commands/core-migration-runner/core-migration-runner.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceVersionModule } from 'src/engine/workspace-manager/workspace-version/workspace-version.module';
@@ -19,6 +20,7 @@ import { WorkspaceVersionModule } from 'src/engine/workspace-manager/workspace-v
CoreEngineVersionModule,
CoreMigrationRunnerModule,
WorkspaceVersionModule,
WorkspaceIteratorModule,
],
providers: [UpgradeCommand],
})
@@ -3,16 +3,15 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { type Repository } from 'typeorm';
import { type ActiveOrSuspendedWorkspacesMigrationCommandOptions } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import {
type AllCommands,
UpgradeCommandRunner,
type VersionCommands,
} from 'src/database/commands/command-runners/upgrade.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
import { BackfillCommandMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-command-menu-items.command';
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command';
import { AddGlobalKeyValuePairUniqueIndexCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-add-global-key-value-pair-unique-index.command';
import { BackfillSelectFieldOptionIdsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command';
import { DeleteOrphanNavigationMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-delete-orphan-navigation-menu-items.command';
import { IdentifyFieldPermissionMetadataCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-identify-field-permission-metadata.command';
@@ -26,16 +25,14 @@ import { MigrateMessagingInfrastructureToMetadataCommand } from 'src/database/co
import { MigrateRichTextToTextCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-migrate-rich-text-to-text.command';
import { SeedCliApplicationRegistrationCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-seed-cli-application-registration.command';
import { UpdateStandardIndexViewNamesCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-update-standard-index-view-names.command';
import { AddGlobalKeyValuePairUniqueIndexCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-add-global-key-value-pair-unique-index.command';
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
import { DeduplicateEngineCommandsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-deduplicate-engine-commands.command';
import { MigrateAiAgentTextToJsonResponseFormatCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-migrate-ai-agent-text-to-json-response-format.command';
import { UpdateEditLayoutCommandMenuItemLabelCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-update-edit-layout-command-menu-item-label.command';
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
@Command({
@@ -48,46 +45,42 @@ export class UpgradeCommand extends UpgradeCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyConfigService: TwentyConfigService,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly coreEngineVersionService: CoreEngineVersionService,
protected readonly workspaceVersionService: WorkspaceVersionService,
protected readonly coreMigrationRunnerService: CoreMigrationRunnerService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
// 1.20 Commands
protected readonly identifyPermissionFlagMetadataCommand: IdentifyPermissionFlagMetadataCommand,
protected readonly makePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
protected readonly identifyObjectPermissionMetadataCommand: IdentifyObjectPermissionMetadataCommand,
protected readonly makeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
protected readonly identifyFieldPermissionMetadataCommand: IdentifyFieldPermissionMetadataCommand,
protected readonly makeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
protected readonly backfillNavigationMenuItemTypeCommand: BackfillNavigationMenuItemTypeCommand,
protected readonly backfillCommandMenuItemsCommand: BackfillCommandMenuItemsCommand,
protected readonly deleteOrphanNavigationMenuItemsCommand: DeleteOrphanNavigationMenuItemsCommand,
protected readonly seedCliApplicationRegistrationCommand: SeedCliApplicationRegistrationCommand,
protected readonly migrateRichTextToTextCommand: MigrateRichTextToTextCommand,
protected readonly migrateMessagingInfrastructureToMetadataCommand: MigrateMessagingInfrastructureToMetadataCommand,
protected readonly backfillSelectFieldOptionIdsCommand: BackfillSelectFieldOptionIdsCommand,
protected readonly updateStandardIndexViewNamesCommand: UpdateStandardIndexViewNamesCommand,
protected readonly makeWorkflowSearchableCommand: MakeWorkflowSearchableCommand,
private readonly identifyPermissionFlagMetadataCommand: IdentifyPermissionFlagMetadataCommand,
private readonly makePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakePermissionFlagUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
private readonly identifyObjectPermissionMetadataCommand: IdentifyObjectPermissionMetadataCommand,
private readonly makeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
private readonly identifyFieldPermissionMetadataCommand: IdentifyFieldPermissionMetadataCommand,
private readonly makeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
private readonly backfillNavigationMenuItemTypeCommand: BackfillNavigationMenuItemTypeCommand,
private readonly backfillCommandMenuItemsCommand: BackfillCommandMenuItemsCommand,
private readonly deleteOrphanNavigationMenuItemsCommand: DeleteOrphanNavigationMenuItemsCommand,
private readonly seedCliApplicationRegistrationCommand: SeedCliApplicationRegistrationCommand,
private readonly migrateRichTextToTextCommand: MigrateRichTextToTextCommand,
private readonly migrateMessagingInfrastructureToMetadataCommand: MigrateMessagingInfrastructureToMetadataCommand,
private readonly backfillSelectFieldOptionIdsCommand: BackfillSelectFieldOptionIdsCommand,
private readonly updateStandardIndexViewNamesCommand: UpdateStandardIndexViewNamesCommand,
private readonly makeWorkflowSearchableCommand: MakeWorkflowSearchableCommand,
// 1.21 Commands
protected readonly addGlobalKeyValuePairUniqueIndexCommand: AddGlobalKeyValuePairUniqueIndexCommand,
protected readonly backfillDatasourceToWorkspaceCommand: BackfillDatasourceToWorkspaceCommand,
protected readonly backfillPageLayoutsAndFieldsWidgetViewFieldsCommand: BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
protected readonly deduplicateEngineCommandsCommand: DeduplicateEngineCommandsCommand,
protected readonly migrateAiAgentTextToJsonResponseFormatCommand: MigrateAiAgentTextToJsonResponseFormatCommand,
protected readonly updateEditLayoutCommandMenuItemLabelCommand: UpdateEditLayoutCommandMenuItemLabelCommand,
private readonly addGlobalKeyValuePairUniqueIndexCommand: AddGlobalKeyValuePairUniqueIndexCommand,
private readonly backfillDatasourceToWorkspaceCommand: BackfillDatasourceToWorkspaceCommand,
private readonly backfillPageLayoutsAndFieldsWidgetViewFieldsCommand: BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
private readonly deduplicateEngineCommandsCommand: DeduplicateEngineCommandsCommand,
private readonly migrateAiAgentTextToJsonResponseFormatCommand: MigrateAiAgentTextToJsonResponseFormatCommand,
private readonly updateEditLayoutCommandMenuItemLabelCommand: UpdateEditLayoutCommandMenuItemLabelCommand,
) {
super(
workspaceRepository,
twentyConfigService,
globalWorkspaceOrmManager,
dataSourceService,
coreEngineVersionService,
workspaceVersionService,
coreMigrationRunnerService,
workspaceIteratorService,
);
const commands_1200: VersionCommands = [
@@ -126,11 +119,4 @@ export class UpgradeCommand extends UpgradeCommandRunner {
'1.21.0': commands_1210,
};
}
override async runMigrationCommand(
passedParams: string[],
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions,
): Promise<void> {
return await super.runMigrationCommand(passedParams, options);
}
}
@@ -3,6 +3,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { BillingGaugeService } from 'src/engine/core-modules/billing/billing-gauge.service';
import { BillingResolver } from 'src/engine/core-modules/billing/billing.resolver';
import { BillingSyncCustomerDataCommand } from 'src/engine/core-modules/billing/commands/billing-sync-customer-data.command';
@@ -68,6 +69,7 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
DataSourceModule,
MetricsModule,
EnterpriseModule,
WorkspaceIteratorModule,
],
providers: [
BillingSubscriptionService,
@@ -6,29 +6,24 @@ import chalk from 'chalk';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { BillingCustomerEntity } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
import { StripeSubscriptionService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'billing:sync-customer-data',
description: 'Sync customer data from Stripe for all active workspaces',
})
export class BillingSyncCustomerDataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BillingSyncCustomerDataCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly stripeSubscriptionService: StripeSubscriptionService,
@InjectRepository(BillingCustomerEntity)
protected readonly billingCustomerRepository: Repository<BillingCustomerEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -6,35 +6,30 @@ import { Command, Option } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { BillingSubscriptionEntity } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { StripeSubscriptionItemService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription-item.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@Command({
name: 'billing:update-subscription-price',
description: 'Update subscription price',
})
export class BillingUpdateSubscriptionPriceCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class BillingUpdateSubscriptionPriceCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private stripePriceIdToUpdate: string;
private newStripePriceId: string;
private clearUsage = false;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
@InjectRepository(BillingSubscriptionEntity)
protected readonly billingSubscriptionRepository: Repository<BillingSubscriptionEntity>,
private readonly billingSubscriptionService: BillingSubscriptionService,
private readonly stripeSubscriptionItemService: StripeSubscriptionItemService,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super(workspaceIteratorService);
}
@Option({
@@ -1,47 +1,38 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command, Option } from 'nest-commander';
import {
ALL_METADATA_NAME,
type AllMetadataName,
} from 'twenty-shared/metadata';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
type ActiveOrSuspendedWorkspacesMigrationCommandOptions,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
ActiveOrSuspendedWorkspaceCommandRunner,
type ActiveOrSuspendedWorkspaceCommandOptions,
} from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service';
type FlatCacheFlushCommandOptions =
ActiveOrSuspendedWorkspacesMigrationCommandOptions & {
allMetadata?: boolean;
};
type FlatCacheFlushCommandOptions = ActiveOrSuspendedWorkspaceCommandOptions & {
metadataName?: string[];
allMetadata?: boolean;
};
@Command({
name: 'cache:flat-cache-invalidate',
description:
'Flush flat entity cache for specific metadata names and workspaces',
})
export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner<FlatCacheFlushCommandOptions> {
private metadataNames: string[] = [];
export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspaceCommandRunner<FlatCacheFlushCommandOptions> {
private flatMapsKeysToFlush: (keyof AllFlatEntityMaps)[] = [];
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super(workspaceIteratorService);
}
@Option({
@@ -50,10 +41,12 @@ export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspacesMigra
'Metadata name(s) to flush cache for. Can be specified multiple times.',
required: false,
})
parseMetadataName(val: string): string[] {
this.metadataNames.push(val);
parseMetadataName(val: string, previous?: string[]): string[] {
const accumulator = previous ?? [];
return this.metadataNames;
accumulator.push(val);
return accumulator;
}
@Option({
@@ -66,11 +59,13 @@ export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspacesMigra
return true;
}
override async runMigrationCommand(
override async run(
passedParams: string[],
options: FlatCacheFlushCommandOptions,
): Promise<void> {
if (!options.allMetadata && this.metadataNames.length === 0) {
const metadataNames = options.metadataName ?? [];
if (!options.allMetadata && metadataNames.length === 0) {
this.logger.error(
'Either --all-metadata or at least one --metadataName must be provided.',
);
@@ -79,7 +74,7 @@ export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspacesMigra
}
const validatedMetadataNames = this.validateAndExpandMetadataNames({
inputMetadataNames: this.metadataNames,
inputMetadataNames: metadataNames,
allMetadata: options.allMetadata,
});
@@ -95,7 +90,7 @@ export class FlatCacheInvalidateCommand extends ActiveOrSuspendedWorkspacesMigra
`Will flush cache for the following flat maps keys: ${this.flatMapsKeysToFlush.join(', ')}`,
);
await super.runMigrationCommand(passedParams, options);
await super.run(passedParams, options);
}
override async runOnWorkspace({
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -27,6 +28,7 @@ import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/wo
WorkspaceCacheStorageModule,
WorkspaceCacheModule,
TypeOrmModule.forFeature([WorkspaceEntity]),
WorkspaceIteratorModule,
],
providers: [
WorkspaceMigrationRunnerService,
@@ -1,28 +1,20 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { MessagingMessageCleanerService } from 'src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service';
@Command({
name: 'messaging:message-cleaner-remove-orphans',
description: 'Remove orphan message and threads from messaging',
})
export class MessagingMessageCleanerRemoveOrphansCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class MessagingMessageCleanerRemoveOrphansCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly messagingMessageCleanerService: MessagingMessageCleanerService,
protected readonly dataSourceService: DataSourceService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super(workspaceIteratorService);
}
override async runOnWorkspace({
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
@@ -21,6 +22,7 @@ import { MessagingMessageCleanerService } from 'src/modules/messaging/message-cl
FeatureFlagModule,
MessagingCommonModule,
MessageChannelDataAccessModule,
WorkspaceIteratorModule,
],
providers: [
MessagingConnectedAccountDeletionCleanupJob,
@@ -1,12 +1,9 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command, Option } from 'nest-commander';
import { LessThan, Repository } from 'typeorm';
import { LessThan } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import { type WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
@@ -15,16 +12,14 @@ import { type WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/sta
name: 'workflow:delete-workflow-runs',
description: 'Delete all workflow runs',
})
export class DeleteWorkflowRunsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
export class DeleteWorkflowRunsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
private createdBeforeDate: string | undefined;
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
protected readonly workspaceIteratorService: WorkspaceIteratorService,
) {
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
super(workspaceIteratorService);
}
@Option({
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { CacheLockModule } from 'src/engine/core-modules/cache-lock/cache-lock.module';
import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module';
import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module';
@@ -23,6 +24,7 @@ import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runne
CacheLockModule,
MetricsModule,
DataSourceModule,
WorkspaceIteratorModule,
],
providers: [WorkflowRunWorkspaceService, DeleteWorkflowRunsCommand],
exports: [WorkflowRunWorkspaceService, DeleteWorkflowRunsCommand],