Add nextStepIds to trigger (#13413)

We will have floating steps in our workflow with the branch design.
Currently, a step without parent is considered linked to the trigger. We
need to distinguish the 2 cases. Thus this PR:
- add `nextStepIds` to workflowVersion.trigger
- create a command to migrate existing triggers
This commit is contained in:
martmull
2025-07-24 16:31:10 +02:00
committed by GitHub
parent 7ee250efe7
commit 099694411c
4 changed files with 113 additions and 10 deletions

View File

@ -0,0 +1,97 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
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 { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
@Command({
name: 'upgrade:1-2:add-next-step-ids-to-workflow-version-triggers',
description: 'Add next step ids to workflow version triggers',
})
export class AddNextStepIdsToWorkflowVersionTriggers extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
const workflowVersionRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
workspaceId,
'workflowVersion',
{ shouldBypassPermissionChecks: true },
);
const workflowVersions = await workflowVersionRepository.find();
for (const workflowVersion of workflowVersions) {
try {
const { trigger, steps } = workflowVersion;
if (!isDefined(trigger)) {
this.logger.warn(
`Undefined trigger for workflowVersion ${workflowVersion.id}`,
);
continue;
}
if (!isDefined(steps)) {
this.logger.warn(
`Undefined steps for workflowVersion ${workflowVersion.id}`,
);
continue;
}
const rootSteps = this.getRootSteps(steps);
if (rootSteps.length === 0) {
this.logger.warn(
`No root steps found for workflowVersion ${workflowVersion.id}`,
);
continue;
}
await workflowVersionRepository.update(workflowVersion.id, {
trigger: {
...trigger,
nextStepIds: rootSteps.map((step) => step.id),
},
});
} catch (error) {
this.logger.error(
`Error while adding nextStepIds to workflowVersion ${workflowVersion.id}`,
error,
);
}
}
this.logger.log(`${workflowVersions.length} triggers updated`);
}
private getRootSteps(steps: WorkflowAction[]): WorkflowAction[] {
const childIds = new Set<string>();
for (const step of steps) {
step.nextStepIds?.forEach((id) => childIds.add(id));
}
return steps.filter((step) => !childIds.has(step.id));
}
}

View File

@ -3,10 +3,17 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command'; import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-next-step-ids-to-workflow-version-triggers.command';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Workspace], 'core')], imports: [TypeOrmModule.forFeature([Workspace], 'core')],
providers: [RemoveWorkflowRunsWithoutState], providers: [
exports: [RemoveWorkflowRunsWithoutState], RemoveWorkflowRunsWithoutState,
AddNextStepIdsToWorkflowVersionTriggers,
],
exports: [
RemoveWorkflowRunsWithoutState,
AddNextStepIdsToWorkflowVersionTriggers,
],
}) })
export class V1_2_UpgradeVersionCommandModule {} export class V1_2_UpgradeVersionCommandModule {}

View File

@ -32,6 +32,7 @@ import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
import { compareVersionMajorAndMinor } from 'src/utils/version/compare-version-minor-and-major'; import { compareVersionMajorAndMinor } from 'src/utils/version/compare-version-minor-and-major';
import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command'; import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command';
import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-next-step-ids-to-workflow-version-triggers.command';
const execPromise = promisify(exec); const execPromise = promisify(exec);
@ -151,6 +152,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
// 1.2 Commands // 1.2 Commands
protected readonly removeWorkflowRunsWithoutState: RemoveWorkflowRunsWithoutState, protected readonly removeWorkflowRunsWithoutState: RemoveWorkflowRunsWithoutState,
protected readonly addNextStepIdsToWorkflowVersionTriggers: AddNextStepIdsToWorkflowVersionTriggers,
// 1.3 Commands // 1.3 Commands
) { ) {
@ -204,7 +206,10 @@ export class UpgradeCommand extends UpgradeCommandRunner {
}; };
const commands_120: VersionCommands = { const commands_120: VersionCommands = {
beforeSyncMetadata: [this.removeWorkflowRunsWithoutState], beforeSyncMetadata: [
this.removeWorkflowRunsWithoutState,
this.addNextStepIdsToWorkflowVersionTriggers,
],
afterSyncMetadata: [], afterSyncMetadata: [],
}; };

View File

@ -16,6 +16,7 @@ type BaseTrigger = {
name: string; name: string;
type: WorkflowTriggerType; type: WorkflowTriggerType;
settings: BaseWorkflowTriggerSettings; settings: BaseWorkflowTriggerSettings;
nextStepIds?: string[];
}; };
export type WorkflowDatabaseEventTrigger = BaseTrigger & { export type WorkflowDatabaseEventTrigger = BaseTrigger & {
@ -25,11 +26,6 @@ export type WorkflowDatabaseEventTrigger = BaseTrigger & {
}; };
}; };
export enum WorkflowManualTriggerAvailability {
EVERYWHERE = 'EVERYWHERE',
WHEN_RECORD_SELECTED = 'WHEN_RECORD_SELECTED',
}
export type WorkflowManualTrigger = BaseTrigger & { export type WorkflowManualTrigger = BaseTrigger & {
type: WorkflowTriggerType.MANUAL; type: WorkflowTriggerType.MANUAL;
settings: BaseWorkflowTriggerSettings & { settings: BaseWorkflowTriggerSettings & {
@ -77,8 +73,6 @@ export type WorkflowWebhookTrigger = BaseTrigger & {
); );
}; };
export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings'];
export type WorkflowTrigger = export type WorkflowTrigger =
| WorkflowDatabaseEventTrigger | WorkflowDatabaseEventTrigger
| WorkflowManualTrigger | WorkflowManualTrigger