feat(core): Change workflow deletions to soft deletes (#14894)

Adds soft‑deletion support for workflows through a new boolean column `isArchived`.

When a workflow is archived we now set `isArchived` flag to true and the workflows
stays in the database and is omitted from the default workflow listing query.

Archived workflows can be viewed in read-only mode, but they cannot be activated.

Archived workflows are still available by ID and can be invoked as sub-executions,
so existing Execute Workflow nodes continue to work. Execution engine doesn't
care about isArchived flag.

Users can restore workflows via Unarchive action at the UI.
This commit is contained in:
Jaakko Husso
2025-05-06 17:48:24 +03:00
committed by GitHub
parent 32b72011e6
commit 3a13139f78
64 changed files with 1616 additions and 124 deletions

View File

@@ -310,7 +310,14 @@ export class SourceControlImportService {
continue;
}
const existingWorkflow = existingWorkflows.find((e) => e.id === importedWorkflow.id);
importedWorkflow.active = existingWorkflow?.active ?? false;
// Workflow's active status is not saved in the remote workflow files, and the field is missing despite
// IWorkflowToImport having it typed as boolean. Imported workflows are always inactive if they are new,
// and existing workflows use the existing workflow's active status unless they have been archived on the remote.
// In that case, we deactivate the existing workflow on pull and turn it archived.
importedWorkflow.active = existingWorkflow
? existingWorkflow.active && !importedWorkflow.isArchived
: false;
const parentFolderId = importedWorkflow.parentFolderId ?? '';
@@ -353,14 +360,17 @@ export class SourceControlImportService {
// remove active pre-import workflow
this.logger.debug(`Deactivating workflow id ${existingWorkflow.id}`);
await workflowManager.remove(existingWorkflow.id);
// try activating the imported workflow
this.logger.debug(`Reactivating workflow id ${existingWorkflow.id}`);
await workflowManager.add(existingWorkflow.id, 'activate');
// update the versionId of the workflow to match the imported workflow
if (importedWorkflow.active) {
// try activating the imported workflow
this.logger.debug(`Reactivating workflow id ${existingWorkflow.id}`);
await workflowManager.add(existingWorkflow.id, 'activate');
}
} catch (e) {
const error = ensureError(e);
this.logger.error(`Failed to activate workflow ${existingWorkflow.id}`, { error });
} finally {
// update the versionId of the workflow to match the imported workflow
await this.workflowRepository.update(
{ id: existingWorkflow.id },
{ versionId: importedWorkflow.versionId },
@@ -639,7 +649,7 @@ export class SourceControlImportService {
async deleteWorkflowsNotInWorkfolder(user: User, candidates: SourceControlledFile[]) {
for (const candidate of candidates) {
await this.workflowService.delete(user, candidate.id);
await this.workflowService.delete(user, candidate.id, true);
}
}