feat(core): Allow setting folder destination when transferring workflow ownership (#14935)

This commit is contained in:
Ricardo Espinoza
2025-04-28 09:35:02 -04:00
committed by GitHub
parent 0a2b740063
commit dbffcdc2ff
5 changed files with 91 additions and 4 deletions

View File

@@ -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(),
}) {}

View File

@@ -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');

View File

@@ -510,6 +510,7 @@ export class WorkflowsController {
workflowId,
body.destinationProjectId,
body.shareCredentials,
body.destinationParentFolderId,
);
}
}

View File

@@ -36,6 +36,7 @@ describe('EnterpriseWorkflowService', () => {
mock(),
mock(),
mock(),
mock(),
);
});

View File

@@ -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