mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(core): Add endpoint to transfer folder to another project (no-changelog) (#15005)
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user