From 713c3d71fb140df6ea4c70f3d491b1f3b6aa86aa Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 7 Aug 2025 19:03:11 +0200 Subject: [PATCH] Add command to delete workflow runs (#13736) Add `workflow:delete-workflow-runs` command to delete workflow runs Options: - created-before YYYY-MM-DD default to now - standard options from `ActiveOrSuspendedWorkspacesMigrationCommandRunner` commands --- .../command/delete-workflow-runs.command.ts | 80 +++++++++++++++++++ .../workflow-run/workflow-run.module.ts | 15 +++- 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts new file mode 100644 index 0000000000..4c49be4e3a --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts @@ -0,0 +1,80 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command, Option } from 'nest-commander'; +import { LessThan, Repository } from 'typeorm'; + +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +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 { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; + +@Command({ + name: 'workflow:delete-workflow-runs', + description: 'Delete all workflow runs', +}) +export class DeleteWorkflowRunsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + private createdBeforeDate: string | undefined; + + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + @Option({ + flags: '--created-before [created_before]', + description: + 'created before. Delete workflow runs created before that date (YYYY-MM-DD)', + required: false, + }) + parseCreatedBefore(val: string): string | undefined { + const date = new Date(val); + + if (isNaN(date.getTime())) { + throw new Error(`Invalid date format: ${val}`); + } + + const createdBeforeDate = date.toISOString(); + + this.createdBeforeDate = createdBeforeDate; + + return createdBeforeDate; + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + try { + const workflowRunRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workflowRun', + { shouldBypassPermissionChecks: true }, + ); + + const createdAtCondition = { + createdAt: LessThan(this.createdBeforeDate || new Date().toISOString()), + }; + + const workflowRunCount = await workflowRunRepository.count({ + where: createdAtCondition, + }); + + if (!options.dryRun && workflowRunCount > 0) { + await workflowRunRepository.delete(createdAtCondition); + } + + this.logger.log( + `${options.dryRun ? ' (DRY RUN): ' : ''}Deleted ${workflowRunCount} workflow runs`, + ); + } catch (error) { + this.logger.error('Error while deleting workflowRun', error); + } + } +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.module.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.module.ts index 0dd770c7fb..cf9a7527be 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.module.ts @@ -9,16 +9,25 @@ import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/s import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; import { CacheLockModule } from 'src/engine/core-modules/cache-lock/cache-lock.module'; +import { DeleteWorkflowRunsCommand } from 'src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @Module({ imports: [ WorkflowCommonModule, - NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'core'), + NestjsQueryTypeOrmModule.forFeature( + [ObjectMetadataEntity, Workspace], + 'core', + ), RecordPositionModule, CacheLockModule, MetricsModule, ], - providers: [WorkflowRunWorkspaceService, ScopedWorkspaceContextFactory], - exports: [WorkflowRunWorkspaceService], + providers: [ + WorkflowRunWorkspaceService, + ScopedWorkspaceContextFactory, + DeleteWorkflowRunsCommand, + ], + exports: [WorkflowRunWorkspaceService, DeleteWorkflowRunsCommand], }) export class WorkflowRunModule {}