mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(core): Allow setting folder destination when transferring workflow ownership (#14935)
This commit is contained in:
@@ -4,4 +4,5 @@ import { Z } from 'zod-class';
|
||||
export class TransferWorkflowBodyDto extends Z.class({
|
||||
destinationProjectId: z.string(),
|
||||
shareCredentials: z.array(z.string()).optional(),
|
||||
destinationParentFolderId: z.string().optional(),
|
||||
}) {}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||
import { TransferWorkflowError } from '@/errors/response-errors/transfer-workflow.error';
|
||||
import { FolderService } from '@/services/folder.service';
|
||||
import { OwnershipService } from '@/services/ownership.service';
|
||||
import { ProjectService } from '@/services/project.service.ee';
|
||||
|
||||
@@ -43,6 +44,7 @@ export class EnterpriseWorkflowService {
|
||||
private readonly credentialsFinderService: CredentialsFinderService,
|
||||
private readonly enterpriseCredentialsService: EnterpriseCredentialsService,
|
||||
private readonly workflowFinderService: WorkflowFinderService,
|
||||
private readonly folderService: FolderService,
|
||||
) {}
|
||||
|
||||
async shareWithProjects(
|
||||
@@ -265,6 +267,7 @@ export class EnterpriseWorkflowService {
|
||||
workflowId: string,
|
||||
destinationProjectId: string,
|
||||
shareCredentials: string[] = [],
|
||||
destinationParentFolderId?: string,
|
||||
) {
|
||||
// 1. get workflow
|
||||
const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
|
||||
@@ -303,6 +306,21 @@ export class EnterpriseWorkflowService {
|
||||
);
|
||||
}
|
||||
|
||||
let parentFolder = null;
|
||||
|
||||
if (destinationParentFolderId) {
|
||||
try {
|
||||
parentFolder = await this.folderService.findFolderInProjectOrFail(
|
||||
destinationParentFolderId,
|
||||
destinationProjectId,
|
||||
);
|
||||
} catch {
|
||||
throw new TransferWorkflowError(
|
||||
`The destination folder with id "${destinationParentFolderId}" does not exist in the project "${destinationProject.name}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. deactivate workflow if necessary
|
||||
const wasActive = workflow.active;
|
||||
if (wasActive) {
|
||||
@@ -345,10 +363,10 @@ export class EnterpriseWorkflowService {
|
||||
}
|
||||
});
|
||||
|
||||
// 9. detach workflow from parent folder in source project
|
||||
await this.workflowRepository.update({ id: workflow.id }, { parentFolder: null });
|
||||
// 9. Move workflow to the right folder if any
|
||||
await this.workflowRepository.update({ id: workflow.id }, { parentFolder });
|
||||
|
||||
// 9. try to activate it again if it was active
|
||||
// 10. try to activate it again if it was active
|
||||
if (wasActive) {
|
||||
try {
|
||||
await this.activeWorkflowManager.add(workflowId, 'update');
|
||||
|
||||
@@ -510,6 +510,7 @@ export class WorkflowsController {
|
||||
workflowId,
|
||||
body.destinationProjectId,
|
||||
body.shareCredentials,
|
||||
body.destinationParentFolderId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ describe('EnterpriseWorkflowService', () => {
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1620,7 +1620,7 @@ describe('PUT /:workflowId/transfer', () => {
|
||||
expect(activeWorkflowManager.add).toHaveBeenCalledWith(workflow.id, 'update');
|
||||
});
|
||||
|
||||
test('should detach workflow from parent folder in source project', async () => {
|
||||
test('should move workflow to project root if `destinationParentFolderId` is not provided', async () => {
|
||||
//
|
||||
// ARRANGE
|
||||
//
|
||||
@@ -1652,6 +1652,72 @@ describe('PUT /:workflowId/transfer', () => {
|
||||
expect(workflowFromDB.parentFolder).toBeNull();
|
||||
});
|
||||
|
||||
test('should move workflow to the parent folder in source project if `destinationParentFolderId` is provided', async () => {
|
||||
//
|
||||
// ARRANGE
|
||||
//
|
||||
const destinationProject = await createTeamProject('Team Project', member);
|
||||
|
||||
const folder = await createFolder(destinationProject, { name: 'Test Folder' });
|
||||
|
||||
const workflow = await createWorkflow({ active: true, parentFolder: folder }, member);
|
||||
|
||||
//
|
||||
// ACT
|
||||
//
|
||||
const response = await testServer
|
||||
.authAgentFor(member)
|
||||
.put(`/workflows/${workflow.id}/transfer`)
|
||||
.send({ destinationProjectId: destinationProject.id, destinationParentFolderId: folder.id })
|
||||
.expect(200);
|
||||
|
||||
//
|
||||
// ASSERT
|
||||
//
|
||||
expect(response.body).toEqual({});
|
||||
|
||||
const workflowFromDB = await workflowRepository.findOneOrFail({
|
||||
where: { id: workflow.id },
|
||||
relations: ['parentFolder'],
|
||||
});
|
||||
|
||||
expect(workflowFromDB.parentFolder?.id).toBe(folder.id);
|
||||
});
|
||||
|
||||
test('should fail destination parent folder does not exist in project', async () => {
|
||||
//
|
||||
// ARRANGE
|
||||
//
|
||||
const destinationProject = await createTeamProject('Team Project', member);
|
||||
|
||||
const anotherProject = await createTeamProject('Another Project', member);
|
||||
|
||||
const folderInDestinationProject = await createFolder(destinationProject, {
|
||||
name: 'Test Folder',
|
||||
});
|
||||
|
||||
const anotherFolder = await createFolder(destinationProject, {
|
||||
name: 'Another Test Folder',
|
||||
});
|
||||
|
||||
const workflow = await createWorkflow(
|
||||
{ active: true, parentFolder: folderInDestinationProject },
|
||||
member,
|
||||
);
|
||||
|
||||
//
|
||||
// ACT
|
||||
//
|
||||
await testServer
|
||||
.authAgentFor(member)
|
||||
.put(`/workflows/${workflow.id}/transfer`)
|
||||
.send({
|
||||
destinationProjectId: anotherProject.id,
|
||||
destinationParentFolderId: anotherFolder.id,
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
test('deactivates the workflow if it cannot be added to the active workflow manager again and returns the WorkflowActivationError as data', async () => {
|
||||
//
|
||||
// ARRANGE
|
||||
|
||||
Reference in New Issue
Block a user