mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(core): Transfer folder structure when deleting a project (no-changelog) (#13865)
This commit is contained in:
@@ -148,6 +148,17 @@ export class FolderService {
|
||||
});
|
||||
}
|
||||
|
||||
async transferFoldersToProject(fromProjectId: string, toProjectId: string) {
|
||||
return await this.folderRepository.update(
|
||||
{
|
||||
homeProject: { id: fromProjectId },
|
||||
},
|
||||
{
|
||||
homeProject: { id: toProjectId },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private transformFolderPathToTree(flatPath: FolderPathRow[]): SimpleFolderNode[] {
|
||||
if (!flatPath || flatPath.length === 0) {
|
||||
return [];
|
||||
|
||||
@@ -61,6 +61,12 @@ export class ProjectService {
|
||||
);
|
||||
}
|
||||
|
||||
private get folderService() {
|
||||
return import('@/services/folder.service').then(({ FolderService }) =>
|
||||
Container.get(FolderService),
|
||||
);
|
||||
}
|
||||
|
||||
async deleteProject(
|
||||
user: User,
|
||||
projectId: string,
|
||||
@@ -134,16 +140,22 @@ export class ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. delete shared credentials into this project
|
||||
// 3. Move folders over to the target project, before deleting the project else cascading will delete workflows
|
||||
if (targetProject) {
|
||||
const folderService = await this.folderService;
|
||||
await folderService.transferFoldersToProject(project.id, targetProject.id);
|
||||
}
|
||||
|
||||
// 4. delete shared credentials into this project
|
||||
// Cascading deletes take care of this.
|
||||
|
||||
// 4. delete shared workflows into this project
|
||||
// 5. delete shared workflows into this project
|
||||
// Cascading deletes take care of this.
|
||||
|
||||
// 5. delete project
|
||||
// 6. delete project
|
||||
await this.projectRepository.remove(project);
|
||||
|
||||
// 6. delete project relations
|
||||
// 7. delete project relations
|
||||
// Cascading deletes take care of this.
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EntityNotFoundError } from '@n8n/typeorm';
|
||||
import { ActiveWorkflowManager } from '@/active-workflow-manager';
|
||||
import type { Project } from '@/databases/entities/project';
|
||||
import type { GlobalRole } from '@/databases/entities/user';
|
||||
import { FolderRepository } from '@/databases/repositories/folder.repository';
|
||||
import { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
|
||||
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
||||
import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository';
|
||||
@@ -13,6 +14,7 @@ import { SharedWorkflowRepository } from '@/databases/repositories/shared-workfl
|
||||
import { getWorkflowById } from '@/public-api/v1/handlers/workflows/workflows.service';
|
||||
import { CacheService } from '@/services/cache/cache.service';
|
||||
import { RoleService } from '@/services/role.service';
|
||||
import { createFolder } from '@test-integration/db/folders';
|
||||
|
||||
import {
|
||||
getCredentialById,
|
||||
@@ -1048,7 +1050,7 @@ describe('DELETE /project/:projectId', () => {
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
test('migrates workflows and credentials to another project if `migrateToProject` is passed', async () => {
|
||||
test('migrates folders, workflows and credentials to another project if `migrateToProject` is passed', async () => {
|
||||
//
|
||||
// ARRANGE
|
||||
//
|
||||
@@ -1071,6 +1073,11 @@ describe('DELETE /project/:projectId', () => {
|
||||
{ project: otherProject, role: 'workflow:editor' },
|
||||
]);
|
||||
|
||||
await createFolder(projectToBeDeleted, { name: 'folder1' });
|
||||
await createFolder(projectToBeDeleted, { name: 'folder2' });
|
||||
await createFolder(targetProject, { name: 'folder1' });
|
||||
await createFolder(otherProject, { name: 'folder3' });
|
||||
|
||||
//
|
||||
// ACT
|
||||
//
|
||||
@@ -1128,6 +1135,22 @@ describe('DELETE /project/:projectId', () => {
|
||||
role: 'credential:user',
|
||||
}),
|
||||
).resolves.toBeDefined();
|
||||
|
||||
// folders are in the target project
|
||||
const foldersInTargetProject = await Container.get(FolderRepository).findBy({
|
||||
homeProject: { id: targetProject.id },
|
||||
});
|
||||
|
||||
const foldersInDeletedProject = await Container.get(FolderRepository).findBy({
|
||||
homeProject: { id: projectToBeDeleted.id },
|
||||
});
|
||||
|
||||
expect(foldersInDeletedProject).toHaveLength(0);
|
||||
|
||||
expect(foldersInTargetProject).toHaveLength(3);
|
||||
expect(foldersInTargetProject.map((f) => f.name)).toEqual(
|
||||
expect.arrayContaining(['folder1', 'folder1', 'folder2']),
|
||||
);
|
||||
});
|
||||
|
||||
// This test is testing behavior that is explicitly not enabled right now,
|
||||
|
||||
Reference in New Issue
Block a user