feat(core): Add endpoint to transfer folder to another project (no-changelog) (#15005)

This commit is contained in:
Ricardo Espinoza
2025-05-07 07:51:03 -04:00
committed by GitHub
parent 1122ee7ec9
commit 715127fa87
11 changed files with 1063 additions and 66 deletions

View File

@@ -1,18 +1,27 @@
import type { Project } from '@n8n/db';
import type { Project, ProjectRole } from '@n8n/db';
import type { User } from '@n8n/db';
import { FolderRepository } from '@n8n/db';
import { ProjectRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { DateTime } from 'luxon';
import { PROJECT_ROOT } from 'n8n-workflow';
import { ApplicationError, PROJECT_ROOT } from 'n8n-workflow';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { mockInstance } from '@test/mocking';
import {
getCredentialSharings,
saveCredential,
shareCredentialWithProjects,
shareCredentialWithUsers,
} from '@test-integration/db/credentials';
import { createFolder } from '@test-integration/db/folders';
import { createTag } from '@test-integration/db/tags';
import { createWorkflow } from '@test-integration/db/workflows';
import { createWorkflow, getWorkflowSharing } from '@test-integration/db/workflows';
import { randomCredentialPayload } from '@test-integration/random';
import { createTeamProject, getPersonalProject, linkUserToProject } from '../shared/db/projects';
import { createOwner, createMember } from '../shared/db/users';
import { createOwner, createMember, createUser, createAdmin } from '../shared/db/users';
import * as testDb from '../shared/test-db';
import type { SuperAgentTest } from '../shared/types';
import * as utils from '../shared/utils/';
@@ -23,6 +32,7 @@ let authOwnerAgent: SuperAgentTest;
let authMemberAgent: SuperAgentTest;
let ownerProject: Project;
let memberProject: Project;
let admin: User;
const testServer = utils.setupTestServer({
endpointGroups: ['folder'],
@@ -32,6 +42,8 @@ let projectRepository: ProjectRepository;
let folderRepository: FolderRepository;
let workflowRepository: WorkflowRepository;
const activeWorkflowManager = mockInstance(ActiveWorkflowManager);
beforeEach(async () => {
await testDb.truncate(['Folder', 'SharedWorkflow', 'TagEntity', 'Project', 'ProjectRelation']);
@@ -46,6 +58,7 @@ beforeEach(async () => {
ownerProject = await getPersonalProject(owner);
memberProject = await getPersonalProject(member);
admin = await createAdmin();
});
describe('POST /projects/:projectId/folders', () => {
@@ -1354,3 +1367,805 @@ describe('GET /projects/:projectId/folders/content', () => {
expect(response.body.data.totalSubFolders).toBe(2);
});
});
describe('PUT /projects/:projectId/folders/:folderId/transfer', () => {
test('cannot transfer into the same project', async () => {
const sourceProject = await createTeamProject('source project', member);
const destinationProject = await createTeamProject('Team Project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
await createWorkflow({ active: true, parentFolder: sourceFolder1 }, destinationProject);
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({ destinationProjectId: destinationProject.id, destinationParentFolderId: '0' })
.expect(400);
});
test('cannot transfer somebody elses folder', async () => {
const sourceProject = await createTeamProject('source project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
await createWorkflow({ parentFolder: sourceFolder1 }, owner);
const destinationProject = await createTeamProject('Team Project', admin);
const destinationFolder1 = await createFolder(destinationProject, { name: 'Source Folder 1' });
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: destinationFolder1,
})
.expect(400);
});
test("cannot transfer if you're not a member of the destination project", async () => {
const sourceProject = await getPersonalProject(member);
const destinationProject = await createTeamProject('Team Project', owner);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
await createWorkflow({ active: true }, destinationProject);
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({ destinationProjectId: destinationProject.id, destinationParentFolderId: '0' })
.expect(404);
});
test.each<ProjectRole>(['project:editor', 'project:viewer'])(
'%ss cannot transfer workflows',
async (projectRole) => {
//
// ARRANGE
//
const sourceProject = await createTeamProject();
await linkUserToProject(member, sourceProject, projectRole);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
await createWorkflow({}, sourceProject);
const destinationProject = await createTeamProject();
await linkUserToProject(member, destinationProject, 'project:admin');
//
// ACT & ASSERT
//
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({ destinationProjectId: destinationProject.id, destinationParentFolderId: '0' })
.expect(403);
},
);
test.each<
[
// user role
'owners' | 'admins',
// source project type
'team' | 'personal',
// destination project type
'team' | 'personal',
// actor
() => User,
// source project
() => Promise<Project> | Project,
// destination project
() => Promise<Project> | Project,
]
>([
// owner
[
'owners',
'team',
'team',
() => owner,
async () => await createTeamProject('Source Project'),
async () => await createTeamProject('Destination Project'),
],
[
'owners',
'team',
'personal',
() => owner,
async () => await createTeamProject('Source Project'),
() => memberProject,
],
[
'owners',
'personal',
'team',
() => owner,
() => memberProject,
async () => await createTeamProject('Destination Project'),
],
// admin
[
'admins',
'team',
'team',
() => admin,
async () => await createTeamProject('Source Project'),
async () => await createTeamProject('Destination Project'),
],
[
'admins',
'team',
'personal',
() => admin,
async () => await createTeamProject('Source Project'),
() => memberProject,
],
[
'admins',
'personal',
'team',
() => admin,
() => memberProject,
async () => await createTeamProject('Destination Project'),
],
])(
'global %s can transfer workflows from a %s project to a %s project',
async (
_roleName,
_sourceProjectName,
_destinationProjectName,
getActor,
getSourceProject,
getDestinationProject,
) => {
// ARRANGE
const actor = getActor();
const sourceProject = await getSourceProject();
const destinationProject = await getDestinationProject();
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const workflow = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
// ACT
const response = await testServer
.authAgentFor(actor)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({ destinationProjectId: destinationProject.id, destinationParentFolderId: '0' })
.expect(200);
// ASSERT
expect(response.body).toEqual({});
const allSharings = await getWorkflowSharing(workflow);
expect(allSharings).toHaveLength(1);
expect(allSharings[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow.id,
role: 'workflow:owner',
});
},
);
test('owner transfers folder from project they are not part of, e.g. test global cred sharing scope', async () => {
// ARRANGE
const admin = await createUser({ role: 'global:admin' });
const sourceProject = await createTeamProject('source project', admin);
const destinationProject = await createTeamProject('destination project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
project: sourceProject,
role: 'credential:owner',
});
// ACT
await testServer
.authAgentFor(owner)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: destinationProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('admin transfers folder from project they are not part of, e.g. test global cred sharing scope', async () => {
// ARRANGE
const admin = await createUser({ role: 'global:admin' });
const sourceProject = await createTeamProject('source project', owner);
const destinationProject = await createTeamProject('destination project', owner);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
project: sourceProject,
role: 'credential:owner',
});
// ACT
await testServer
.authAgentFor(admin)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: destinationProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('member transfers folder from personal project to team project and one workflow contains a credential that they can use but not share', async () => {
// ARRANGE
const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
const sourceProject = await projectRepository.getPersonalProjectForUserOrFail(member.id);
const destinationProject = await createTeamProject('destination project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
user: owner,
role: 'credential:owner',
});
await shareCredentialWithUsers(credential, [member]);
// ACT
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: ownerPersonalProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('member transfers folder from their personal project to another team project in which they have editor role', async () => {
// ARRANGE
const sourceProject = await projectRepository.getPersonalProjectForUserOrFail(member.id);
const destinationProject = await createTeamProject('destination project');
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
project: sourceProject,
role: 'credential:owner',
});
await linkUserToProject(member, destinationProject, 'project:editor');
// ACT
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: destinationProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('member transfers folder from a team project as project admin to another team project in which they have editor role', async () => {
// ARRANGE
const sourceProject = await createTeamProject('source project', member);
const destinationProject = await createTeamProject('destination project');
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
project: sourceProject,
role: 'credential:owner',
});
await linkUserToProject(member, destinationProject, 'project:editor');
// ACT
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: destinationProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('member transfers workflow from a team project as project admin to another team project in which they have editor role but cannot share the credential that is only shared into the source project', async () => {
// ARRANGE
const sourceProject = await createTeamProject('source project', member);
const destinationProject = await createTeamProject('destination project');
const ownerProject = await getPersonalProject(owner);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
user: owner,
role: 'credential:owner',
});
await linkUserToProject(member, destinationProject, 'project:editor');
await shareCredentialWithProjects(credential, [sourceProject]);
// ACT
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: ownerProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('member transfers workflow from a team project as project admin to another team project in which they have editor role but cannot share all the credentials', async () => {
// ARRANGE
const sourceProject = await createTeamProject('source project', member);
const destinationProject = await createTeamProject('destination project');
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
const workflow1 = await createWorkflow({ parentFolder: sourceFolder1 }, sourceProject);
const workflow2 = await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
const credential = await saveCredential(randomCredentialPayload(), {
project: sourceProject,
role: 'credential:owner',
});
const ownersCredential = await saveCredential(randomCredentialPayload(), {
user: owner,
role: 'credential:owner',
});
await linkUserToProject(member, destinationProject, 'project:editor');
// ACT
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
shareCredentials: [credential.id, ownersCredential.id],
})
.expect(200);
// ASSERT
const workflow1Sharing = await getWorkflowSharing(workflow1);
expect(workflow1Sharing).toHaveLength(1);
expect(workflow1Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow1.id,
role: 'workflow:owner',
});
const workflow2Sharing = await getWorkflowSharing(workflow2);
expect(workflow2Sharing).toHaveLength(1);
expect(workflow2Sharing[0]).toMatchObject({
projectId: destinationProject.id,
workflowId: workflow2.id,
role: 'workflow:owner',
});
const sourceFolderInDb = await folderRepository.findOne({
where: { id: sourceFolder1.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolderInDb).toBeDefined();
expect(sourceFolderInDb?.parentFolder).toBeNull();
expect(sourceFolderInDb?.homeProject.id).toBe(destinationProject.id);
const sourceFolder2InDb = await folderRepository.findOne({
where: { id: sourceFolder2.id },
relations: ['parentFolder', 'homeProject'],
});
expect(sourceFolder2InDb).toBeDefined();
expect(sourceFolder2InDb?.parentFolder?.id).toBe(sourceFolder1.id);
expect(sourceFolder2InDb?.homeProject.id).toBe(destinationProject.id);
const allCredentialSharings = await getCredentialSharings(credential);
expect(allCredentialSharings).toHaveLength(2);
expect(allCredentialSharings).toEqual(
expect.arrayContaining([
expect.objectContaining({
projectId: sourceProject.id,
credentialsId: credential.id,
role: 'credential:owner',
}),
expect.objectContaining({
projectId: destinationProject.id,
credentialsId: credential.id,
role: 'credential:user',
}),
]),
);
});
test('returns a 500 if the workflow cannot be activated due to an unknown error', async () => {
//
// ARRANGE
//
const sourceProject = await createTeamProject('source project', member);
const destinationProject = await createTeamProject('Team Project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
const sourceFolder2 = await createFolder(sourceProject, {
name: 'Source Folder 2',
parentFolder: sourceFolder1,
});
await createWorkflow({ active: true, parentFolder: sourceFolder1 }, sourceProject);
await createWorkflow({ parentFolder: sourceFolder2 }, sourceProject);
activeWorkflowManager.add.mockRejectedValue(new ApplicationError('Oh no!'));
//
// ACT & ASSERT
//
await testServer
.authAgentFor(member)
.put(`/projects/${sourceProject.id}/folders/${sourceFolder1.id}/transfer`)
.send({
destinationProjectId: destinationProject.id,
destinationParentFolderId: '0',
})
.expect(500);
});
});