Skip to content

Commit

Permalink
fix(editor): Allow resources to move between personal and team projec…
Browse files Browse the repository at this point in the history
…ts (#10683)

Co-authored-by: Danny Martini <danny@n8n.io>
  • Loading branch information
cstuncsik and despairblue authored Sep 27, 2024
1 parent 2df5a5b commit 136d491
Show file tree
Hide file tree
Showing 31 changed files with 823 additions and 716 deletions.
2 changes: 0 additions & 2 deletions cypress/composables/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export const addProjectMember = (email: string, role?: string) => {
}
};
export const getResourceMoveModal = () => cy.getByTestId('project-move-resource-modal');
export const getResourceMoveConfirmModal = () =>
cy.getByTestId('project-move-resource-confirm-modal');
export const getProjectMoveSelect = () => cy.getByTestId('project-move-resource-modal-select');

export function createProject(name: string) {
Expand Down
261 changes: 210 additions & 51 deletions cypress/e2e/39-projects.cy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import * as projects from '../composables/projects';
import { INSTANCE_MEMBERS, MANUAL_TRIGGER_NODE_NAME, NOTION_NODE_NAME } from '../constants';
import {
INSTANCE_ADMIN,
INSTANCE_MEMBERS,
INSTANCE_OWNER,
MANUAL_TRIGGER_NODE_NAME,
NOTION_NODE_NAME,
} from '../constants';
import {
WorkflowsPage,
WorkflowPage,
Expand Down Expand Up @@ -481,54 +487,93 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Next")')
.find('button:contains("Move workflow")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 2)
.first()
.should('contain.text', 'Project 1')
.should('have.length', 5)
.filter(':contains("Project 1")')
.click();
projects.getResourceMoveModal().find('button:contains("Next")').click();
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();

workflowsPage.getters
.workflowCards()
.should('have.length', 3)
.filter(':contains("Owned by me")')
.should('not.exist');

// Move the workflow from Project 1 to Project 2
projects.getMenuItems().first().click();
workflowsPage.getters.workflowCards().should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();

projects
.getResourceMoveConfirmModal()
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Confirm")')
.find('button:contains("Move workflow")')
.should('be.disabled');

projects
.getResourceMoveConfirmModal()
.find('input[type="checkbox"]')
.first()
.parents('label')
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 2")')
.click();
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();

// Move the workflow from Project 2 to a member user
projects.getMenuItems().last().click();
workflowsPage.getters.workflowCards().should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();

projects
.getResourceMoveConfirmModal()
.find('button:contains("Confirm")')
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.should('be.disabled');
projects
.getResourceMoveConfirmModal()
.find('input[type="checkbox"]')
.last()
.parents('label')
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_MEMBERS[0].email}")`)
.click();

projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
workflowsPage.getters.workflowCards().should('have.length', 1);

// Move the workflow from member user back to Home
projects.getHomeButton().click();
workflowsPage.getters
.workflowCards()
.should('have.length', 3)
.filter(':has(.n8n-badge:contains("Project"))')
.should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();

projects
.getResourceMoveConfirmModal()
.find('button:contains("Confirm")')
.should('not.be.disabled')
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_OWNER.email}")`)
.click();

projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
workflowsPage.getters
.workflowCards()
.should('have.length', 3)
.filter(':contains("Owned by me")')
.should('not.exist');
.should('have.length', 1);

// Move the credential from Project 1 to Project 2
projects.getMenuItems().first().click();
workflowsPage.getters.workflowCards().should('have.length', 2);
projects.getProjectTabCredentials().click();
credentialsPage.getters.credentialCards().should('have.length', 1);
credentialsPage.getters.credentialCardActions('Credential in Project 1').click();
Expand All @@ -537,48 +582,162 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Next")')
.find('button:contains("Move credential")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 1)
.first()
.should('contain.text', 'Project 2')
.should('have.length', 5)
.filter(':contains("Project 2")')
.click();
projects.getResourceMoveModal().find('button:contains("Next")').click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();

credentialsPage.getters.credentialCards().should('not.have.length');

// Move the credential from Project 2 to admin user
projects.getMenuItems().last().click();
projects.getProjectTabCredentials().click();
credentialsPage.getters.credentialCards().should('have.length', 2);

credentialsPage.getters.credentialCardActions('Credential in Project 1').click();
credentialsPage.getters.credentialMoveButton().click();

projects
.getResourceMoveConfirmModal()
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Confirm")')
.find('button:contains("Move credential")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_ADMIN.email}")`)
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();
credentialsPage.getters.credentialCards().should('have.length', 1);

// Move the credential from admin user back to instance owner
projects.getHomeButton().click();
projects.getProjectTabCredentials().click();
credentialsPage.getters.credentialCards().should('have.length', 3);

credentialsPage.getters.credentialCardActions('Credential in Project 1').click();
credentialsPage.getters.credentialMoveButton().click();

projects
.getResourceMoveConfirmModal()
.find('input[type="checkbox"]')
.first()
.parents('label')
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_OWNER.email}")`)
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();

credentialsPage.getters
.credentialCards()
.should('have.length', 3)
.filter(':contains("Owned by me")')
.should('have.length', 2);

// Move the credential from admin user back to its original project (Project 1)
credentialsPage.getters.credentialCardActions('Credential in Project 1').click();
credentialsPage.getters.credentialMoveButton().click();

projects
.getResourceMoveConfirmModal()
.find('button:contains("Confirm")')
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.should('be.disabled');
projects
.getResourceMoveConfirmModal()
.find('input[type="checkbox"]')
.last()
.parents('label')
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 1")')
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();

projects.getMenuItems().first().click();
projects.getProjectTabCredentials().click();
credentialsPage.getters
.credentialCards()
.filter(':contains("Credential in Project 1")')
.should('have.length', 1);
});

it('should allow to change inaccessible credential when the workflow was moved to a team project', () => {
cy.signinAsOwner();
cy.visit(workflowsPage.url);

// Create a credential in the Home project
projects.getProjectTabCredentials().should('be.visible').click();
credentialsPage.getters.emptyListCreateCredentialButton().click();
projects.createCredential('Credential in Home project');

// Create a workflow in the Home project
projects.getHomeButton().click();
workflowsPage.getters.workflowCards().should('not.have.length');
workflowsPage.getters.newWorkflowButtonCard().click();
workflowsPage.getters.workflowCards().should('not.have.length');

workflowsPage.getters.newWorkflowButtonCard().click();
workflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
workflowPage.actions.addNodeToCanvas(NOTION_NODE_NAME, true, true);
ndv.getters.backToCanvas().click();
workflowPage.actions.saveWorkflowOnButtonClick();

// Create a project and add a user to it
projects.createProject('Project 1');
projects.addProjectMember(INSTANCE_MEMBERS[0].email);
projects.getProjectSettingsSaveButton().click();

// Move the workflow from Home to Project 1
projects.getHomeButton().click();
workflowsPage.getters
.workflowCards()
.should('have.length', 1)
.filter(':contains("Owned by me")')
.should('exist');
workflowsPage.getters.workflowCardActions('My workflow').click();
workflowsPage.getters.workflowMoveButton().click();

projects
.getResourceMoveConfirmModal()
.find('button:contains("Confirm")')
.should('not.be.disabled')
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 4)
.filter(':contains("Project 1")')
.click();
credentialsPage.getters.credentialCards().should('not.have.length');
projects.getMenuItems().last().click();
projects.getProjectTabCredentials().click();
credentialsPage.getters.credentialCards().should('have.length', 2);
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();

workflowsPage.getters
.workflowCards()
.should('have.length', 1)
.filter(':contains("Owned by me")')
.should('not.exist');

//Log out with instance owner and log in with the member user
mainSidebar.actions.openUserMenu();
cy.getByTestId('user-menu-item-logout').click();

cy.get('input[name="email"]').type(INSTANCE_MEMBERS[0].email);
cy.get('input[name="password"]').type(INSTANCE_MEMBERS[0].password);
cy.getByTestId('form-submit-button').click();

// Open the moved workflow
workflowsPage.getters.workflowCards().should('have.length', 1);
workflowsPage.getters.workflowCards().first().click();

// Check if the credential can be changed
workflowPage.getters.canvasNodeByName(NOTION_NODE_NAME).should('be.visible').dblclick();
ndv.getters.credentialInput().find('input').should('be.enabled');
});

it('should handle viewer role', () => {
Expand Down
8 changes: 0 additions & 8 deletions packages/cli/src/credentials/credentials.service.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,6 @@ export class EnterpriseCredentialsService {
"You can't transfer a credential into the project that's already owning it.",
);
}
if (sourceProject.type !== 'team' && sourceProject.type !== 'personal') {
throw new TransferCredentialError(
'You can only transfer credentials out of personal or team projects.',
);
}
if (destinationProject.type !== 'team') {
throw new TransferCredentialError('You can only transfer credentials into team projects.');
}

await this.sharedCredentialsRepository.manager.transaction(async (trx) => {
// 6. transfer the credential
Expand Down
8 changes: 0 additions & 8 deletions packages/cli/src/workflows/workflow.service.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,6 @@ export class EnterpriseWorkflowService {
"You can't transfer a workflow into the project that's already owning it.",
);
}
if (sourceProject.type !== 'team' && sourceProject.type !== 'personal') {
throw new TransferWorkflowError(
'You can only transfer workflows out of personal or team projects.',
);
}
if (destinationProject.type !== 'team') {
throw new TransferWorkflowError('You can only transfer workflows into team projects.');
}

// 6. deactivate workflow if necessary
const wasActive = workflow.active;
Expand Down
Loading

0 comments on commit 136d491

Please sign in to comment.