[Fix] Command to migrate operand values for workflows (#14849)
In [this PR](https://github.com/twentyhq/twenty/pull/14785) we got rid of what we now call ViewFilterOperandDeprecated, a camelCase version of ViewFilterOperand, which we thought we only used in the FE. We did not notice that this enum was used to persist filters used in workflows, reflected in workflowVersion and workflowRun. As a result workflow runs were broken. [In this mitigation PR](https://github.com/twentyhq/twenty/pull/14837) (and [this one](https://github.com/twentyhq/twenty/pull/14841)) we updated the code handle both enum values from ViewFilterOperandDeprecated and ViewFilterOperand, but we still want to get rid of ViewFilterOperandDeprecated. the command in this PR replaces the occurences of enum values of ViewFilterOperandDeprecated. When this has been merged, deployed and run on the workspaces, we will be able to remove ViewFilterOperandDeprecated altogether; that will have to be done in 1.10 though not before.
This commit is contained in:
+210
@@ -0,0 +1,210 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isString } from 'class-validator';
|
||||
import { Command } from 'nest-commander';
|
||||
import {
|
||||
convertViewFilterOperandToCoreOperand,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { In, Raw, Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
type RunOnWorkspaceArgs,
|
||||
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowRunState,
|
||||
WorkflowRunWorkspaceEntity,
|
||||
} from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
|
||||
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { isWorkflowFilterAction } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/guards/is-workflow-filter-action.guard';
|
||||
import { isWorkflowFindRecordsAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-find-records-action.guard';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
@Command({
|
||||
name: 'upgrade:1-10:migrate-workflow-step-filter-operand-value',
|
||||
description:
|
||||
'Migrate workflowVersion.steps[].settings.input.stepFilters[].operand to use new operand enum',
|
||||
})
|
||||
export class MigrateWorkflowStepFilterOperandValueCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace)
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
index,
|
||||
total,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
this.logger.log(
|
||||
`[${index + 1}/${total}] Migrating workflow step filter operand values for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
// workflowVersions
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflowVersionsToMigrate = await workflowVersionRepository.find({
|
||||
where: [
|
||||
{
|
||||
steps: Raw(
|
||||
(_alias) => `"workflowVersion"."steps"::text LIKE :search`,
|
||||
{
|
||||
search: '%operand%',
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Found ${workflowVersionsToMigrate.length} workflowVersions to migrate`,
|
||||
);
|
||||
|
||||
for (const workflowVersion of workflowVersionsToMigrate) {
|
||||
let steps: WorkflowAction[] | null | undefined;
|
||||
|
||||
steps = workflowVersion.steps;
|
||||
|
||||
let hasChanged = false;
|
||||
|
||||
for (const step of steps ?? []) {
|
||||
if (isWorkflowFilterAction(step)) {
|
||||
for (const filter of step.settings.input.stepFilters ?? []) {
|
||||
if (isDefined(filter.operand) && isString(filter.operand)) {
|
||||
const newOperand = convertViewFilterOperandToCoreOperand(
|
||||
filter.operand,
|
||||
);
|
||||
|
||||
if (newOperand && newOperand !== filter.operand) {
|
||||
filter.operand = newOperand;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isWorkflowFindRecordsAction(step)) {
|
||||
for (const filter of step.settings.input.filter?.recordFilters ??
|
||||
[]) {
|
||||
if (isString(filter.operand)) {
|
||||
const newOperand = convertViewFilterOperandToCoreOperand(
|
||||
filter.operand,
|
||||
);
|
||||
|
||||
if (newOperand && newOperand !== filter.operand) {
|
||||
filter.operand = newOperand;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
this.logger.log(
|
||||
`${options.dryRun ? 'DRY RUN - Would be' : ''}Updating workflowVersion ${workflowVersion.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (!options.dryRun) {
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersion.id },
|
||||
{ steps },
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`${options.dryRun ? 'DRY RUN - Would have' : ''} Updated workflowVersion ${workflowVersion.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//workflowRuns
|
||||
const workflowRunRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowRun',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflowRunsToMigrate = await workflowRunRepository.find({
|
||||
where: {
|
||||
workflowVersionId: In(
|
||||
workflowVersionsToMigrate.map(
|
||||
(workflowVersion) => workflowVersion.id,
|
||||
),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Found ${workflowRunsToMigrate.length} workflowRuns to migrate`,
|
||||
);
|
||||
|
||||
for (const workflowRun of workflowRunsToMigrate) {
|
||||
let state: WorkflowRunState | null | undefined = workflowRun.state;
|
||||
|
||||
if (!isDefined(state)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let hasChanged = false;
|
||||
|
||||
for (const step of state.flow.steps) {
|
||||
if (isWorkflowFindRecordsAction(step)) {
|
||||
const filter = step.settings.input.filter;
|
||||
|
||||
for (const recordFilter of filter?.recordFilters ?? []) {
|
||||
if (isString(recordFilter.operand)) {
|
||||
const newOperand = convertViewFilterOperandToCoreOperand(
|
||||
recordFilter.operand,
|
||||
);
|
||||
|
||||
if (newOperand && newOperand !== recordFilter.operand) {
|
||||
recordFilter.operand = newOperand;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isWorkflowFilterAction(step)) {
|
||||
for (const filter of step.settings.input.stepFilters ?? []) {
|
||||
if (isString(filter.operand)) {
|
||||
const newOperand = convertViewFilterOperandToCoreOperand(
|
||||
filter.operand,
|
||||
);
|
||||
|
||||
if (newOperand && newOperand !== filter.operand) {
|
||||
filter.operand = newOperand;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
this.logger.log(
|
||||
`${options.dryRun ? 'DRY RUN - Would be' : ''}Updating workflowRun ${workflowRun.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (!options.dryRun) {
|
||||
await workflowRunRepository.update({ id: workflowRun.id }, { state });
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`${options.dryRun ? 'DRY RUN - Would have' : ''}Updated workflowRun ${workflowRun.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { MigrateWorkflowStepFilterOperandValueCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-workflow-step-filter-operand-value';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Workspace]), WorkspaceDataSourceModule],
|
||||
providers: [MigrateWorkflowStepFilterOperandValueCommand],
|
||||
exports: [MigrateWorkflowStepFilterOperandValueCommand],
|
||||
})
|
||||
export class V1_10_UpgradeVersionCommandModule {}
|
||||
+2
@@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { V0_54_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-54/0-54-upgrade-version-command.module';
|
||||
import { V0_55_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-55/0-55-upgrade-version-command.module';
|
||||
import { V1_1_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module';
|
||||
import { V1_10_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-10/1-10-upgrade-version-command.module';
|
||||
import { V1_2_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-2/1-2-upgrade-version-command.module';
|
||||
import { V1_3_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module';
|
||||
import { V1_5_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module';
|
||||
@@ -24,6 +25,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
||||
V1_5_UpgradeVersionCommandModule,
|
||||
V1_6_UpgradeVersionCommandModule,
|
||||
V1_7_UpgradeVersionCommandModule,
|
||||
V1_10_UpgradeVersionCommandModule,
|
||||
WorkspaceSyncMetadataModule,
|
||||
],
|
||||
providers: [UpgradeCommand],
|
||||
|
||||
+10
@@ -19,6 +19,7 @@ import { AddEnqueuedStatusToWorkflowRunCommand } from 'src/database/commands/upg
|
||||
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
|
||||
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
|
||||
import { MigrateWorkflowRunStatesCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-workflow-run-state.command';
|
||||
import { MigrateWorkflowStepFilterOperandValueCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-workflow-step-filter-operand-value';
|
||||
import { AddEnqueuedStatusToWorkflowRunV2Command } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-enqueued-status-to-workflow-run-v2.command';
|
||||
import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-next-step-ids-to-workflow-version-triggers.command';
|
||||
import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command';
|
||||
@@ -83,6 +84,9 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
|
||||
// 1.7 Commands
|
||||
protected readonly backfillWorkflowManualTriggerAvailabilityCommand: BackfillWorkflowManualTriggerAvailabilityCommand,
|
||||
|
||||
// 1.10 Commands
|
||||
protected readonly migrateWorkflowStepFilterOperandValueCommand: MigrateWorkflowStepFilterOperandValueCommand,
|
||||
) {
|
||||
super(
|
||||
workspaceRepository,
|
||||
@@ -176,6 +180,11 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
const commands_1100: VersionCommands = {
|
||||
beforeSyncMetadata: [this.migrateWorkflowStepFilterOperandValueCommand],
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
this.allCommands = {
|
||||
'0.53.0': commands_053,
|
||||
'0.54.0': commands_054,
|
||||
@@ -189,6 +198,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
'1.5.0': commands_150,
|
||||
'1.6.0': commands_160,
|
||||
'1.7.0': commands_170,
|
||||
'1.10.0': commands_1100,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,5 +14,5 @@ export enum ViewFilterOperandDeprecated {
|
||||
IsInPast = 'isInPast',
|
||||
IsInFuture = 'isInFuture',
|
||||
IsToday = 'isToday',
|
||||
VectorSearch = 'vectorSearch',
|
||||
VectorSearch = 'search',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user