mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Allow transferring user's data to team project when deleting them (no-changelog) (#13941)
This commit is contained in:
@@ -189,9 +189,9 @@ export class UsersController {
|
|||||||
let transfereeId;
|
let transfereeId;
|
||||||
|
|
||||||
if (transferId) {
|
if (transferId) {
|
||||||
const transfereePersonalProject = await this.projectRepository.findOneBy({ id: transferId });
|
const transfereeProject = await this.projectRepository.findOneBy({ id: transferId });
|
||||||
|
|
||||||
if (!transfereePersonalProject) {
|
if (!transfereeProject) {
|
||||||
throw new NotFoundError(
|
throw new NotFoundError(
|
||||||
'Request to delete a user failed because the transferee project was not found in DB',
|
'Request to delete a user failed because the transferee project was not found in DB',
|
||||||
);
|
);
|
||||||
@@ -199,8 +199,7 @@ export class UsersController {
|
|||||||
|
|
||||||
const transferee = await this.userRepository.findOneByOrFail({
|
const transferee = await this.userRepository.findOneByOrFail({
|
||||||
projectRelations: {
|
projectRelations: {
|
||||||
projectId: transfereePersonalProject.id,
|
projectId: transfereeProject.id,
|
||||||
role: 'project:personalOwner',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -209,25 +208,23 @@ export class UsersController {
|
|||||||
await this.userService.getManager().transaction(async (trx) => {
|
await this.userService.getManager().transaction(async (trx) => {
|
||||||
await this.workflowService.transferAll(
|
await this.workflowService.transferAll(
|
||||||
personalProjectToDelete.id,
|
personalProjectToDelete.id,
|
||||||
transfereePersonalProject.id,
|
transfereeProject.id,
|
||||||
trx,
|
trx,
|
||||||
);
|
);
|
||||||
await this.credentialsService.transferAll(
|
await this.credentialsService.transferAll(
|
||||||
personalProjectToDelete.id,
|
personalProjectToDelete.id,
|
||||||
transfereePersonalProject.id,
|
transfereeProject.id,
|
||||||
trx,
|
trx,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.folderService.transferAllFoldersToProject(
|
await this.folderService.transferAllFoldersToProject(
|
||||||
personalProjectToDelete.id,
|
personalProjectToDelete.id,
|
||||||
transfereePersonalProject.id,
|
transfereeProject.id,
|
||||||
trx,
|
trx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.projectService.clearCredentialCanUseExternalSecretsCache(
|
await this.projectService.clearCredentialCanUseExternalSecretsCache(transfereeProject.id);
|
||||||
transfereePersonalProject.id,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ownedSharedWorkflows, ownedSharedCredentials] = await Promise.all([
|
const [ownedSharedWorkflows, ownedSharedCredentials] = await Promise.all([
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ describe('DELETE /users/:id', () => {
|
|||||||
expect(credential).toBeNull();
|
expect(credential).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should delete user and team relations and transfer their personal resources', async () => {
|
test('should delete user and team relations and transfer their personal resources to user', async () => {
|
||||||
//
|
//
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
//
|
//
|
||||||
@@ -603,6 +603,83 @@ describe('DELETE /users/:id', () => {
|
|||||||
expect(deletedUserFolders).toHaveLength(0);
|
expect(deletedUserFolders).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should delete user and transfer their personal resources to team project', async () => {
|
||||||
|
//
|
||||||
|
// ARRANGE
|
||||||
|
//
|
||||||
|
const memberToDelete = await createMember();
|
||||||
|
|
||||||
|
const teamProject = await createTeamProject('test project', owner);
|
||||||
|
|
||||||
|
const memberPersonalProject = await getPersonalProject(memberToDelete);
|
||||||
|
|
||||||
|
const memberToDeleteWorkflow = await createWorkflow({ name: 'workflow1' }, memberToDelete);
|
||||||
|
const memberToDeleteCredential = await saveCredential(randomCredentialPayload(), {
|
||||||
|
user: memberToDelete,
|
||||||
|
role: 'credential:owner',
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
createFolder(memberPersonalProject, { name: 'folder1' }),
|
||||||
|
createFolder(memberPersonalProject, { name: 'folder2' }),
|
||||||
|
createFolder(teamProject, { name: 'folder3' }),
|
||||||
|
createFolder(teamProject, { name: 'folder1' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const deleteSpy = jest.spyOn(Container.get(CacheService), 'deleteMany');
|
||||||
|
|
||||||
|
//
|
||||||
|
// ACT
|
||||||
|
//
|
||||||
|
await ownerAgent
|
||||||
|
.delete(`/users/${memberToDelete.id}`)
|
||||||
|
.query({ transferId: teamProject.id })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
//
|
||||||
|
// ASSERT
|
||||||
|
//
|
||||||
|
|
||||||
|
deleteSpy.mockClear();
|
||||||
|
|
||||||
|
const sharedWorkflowRepository = Container.get(SharedWorkflowRepository);
|
||||||
|
const sharedCredentialRepository = Container.get(SharedCredentialsRepository);
|
||||||
|
const folderRepository = Container.get(FolderRepository);
|
||||||
|
const userRepository = Container.get(UserRepository);
|
||||||
|
|
||||||
|
// assert member has been deleted
|
||||||
|
const user = await userRepository.findOneBy({ id: memberToDelete.id });
|
||||||
|
expect(user).toBeNull();
|
||||||
|
|
||||||
|
// assert the workflow has been transferred
|
||||||
|
const memberToDeleteWorkflowProjectOwner =
|
||||||
|
await sharedWorkflowRepository.getWorkflowOwningProject(memberToDeleteWorkflow.id);
|
||||||
|
|
||||||
|
expect(memberToDeleteWorkflowProjectOwner?.id).toBe(teamProject.id);
|
||||||
|
|
||||||
|
// assert the credential has been transferred
|
||||||
|
const memberToDeleteCredentialProjectOwner =
|
||||||
|
await sharedCredentialRepository.findCredentialOwningProject(memberToDeleteCredential.id);
|
||||||
|
|
||||||
|
expect(memberToDeleteCredentialProjectOwner?.id).toBe(teamProject.id);
|
||||||
|
|
||||||
|
// assert that the folders have been transferred
|
||||||
|
const transfereeFolders = await folderRepository.findBy({
|
||||||
|
homeProject: { id: teamProject.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
const deletedUserFolders = await folderRepository.findBy({
|
||||||
|
homeProject: { id: memberPersonalProject.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(transfereeFolders).toHaveLength(4);
|
||||||
|
expect(transfereeFolders.map((folder) => folder.name)).toEqual(
|
||||||
|
expect.arrayContaining(['folder1', 'folder2', 'folder3', 'folder1']),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(deletedUserFolders).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('should fail to delete self', async () => {
|
test('should fail to delete self', async () => {
|
||||||
await ownerAgent.delete(`/users/${owner.id}`).expect(400);
|
await ownerAgent.delete(`/users/${owner.id}`).expect(400);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user