feat(core): Allow moving workflow to project root (no-changelog) (#14075)

This commit is contained in:
Ricardo Espinoza
2025-03-20 09:45:10 -04:00
committed by GitHub
parent e06c552a6a
commit 305ea0fb32
2 changed files with 26 additions and 5 deletions

View File

@@ -9,7 +9,7 @@ import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPar
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import { BinaryDataService, Logger } from 'n8n-core';
import { NodeApiError } from 'n8n-workflow';
import { NodeApiError, PROJECT_ROOT } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
@@ -281,8 +281,10 @@ export class WorkflowService {
if (parentFolderId) {
const project = await this.sharedWorkflowRepository.getWorkflowOwningProject(workflow.id);
await this.folderService.findFolderInProjectOrFail(parentFolderId, project?.id ?? '');
updatePayload.parentFolder = { id: parentFolderId };
if (parentFolderId !== PROJECT_ROOT) {
await this.folderService.findFolderInProjectOrFail(parentFolderId, project?.id ?? '');
}
updatePayload.parentFolder = parentFolderId === PROJECT_ROOT ? null : { id: parentFolderId };
}
await this.workflowRepository.update(workflowId, updatePayload);

View File

@@ -1,7 +1,7 @@
import { Container } from '@n8n/di';
import type { Scope } from '@n8n/permissions';
import { DateTime } from 'luxon';
import type { INode, IPinData, IWorkflowBase } from 'n8n-workflow';
import { PROJECT_ROOT, type INode, type IPinData, type IWorkflowBase } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
@@ -2045,7 +2045,7 @@ describe('PATCH /workflows/:workflowId', () => {
expect(updatedWorkflow.meta).toEqual(payload.meta);
});
test('should update workflow parent folder', async () => {
test('should move workflow to folder', async () => {
const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
const folder1 = await createFolder(ownerPersonalProject, { name: 'folder1' });
@@ -2067,6 +2067,25 @@ describe('PATCH /workflows/:workflowId', () => {
expect(updatedWorkflow.parentFolder?.id).toBe(folder1.id);
});
test('should move workflow to project root', async () => {
const workflow = await createWorkflow({}, owner);
const payload = {
versionId: workflow.versionId,
parentFolderId: PROJECT_ROOT,
};
const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload);
expect(response.statusCode).toBe(200);
const updatedWorkflow = await Container.get(WorkflowRepository).findOneOrFail({
where: { id: workflow.id },
relations: ['parentFolder'],
});
expect(updatedWorkflow.parentFolder).toBe(null);
});
test('should fail if trying update workflow parent folder with a folder that does not belong to project', async () => {
const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
const memberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(