From bc35e8c33d470399466514b4d4874c965d7edc08 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Tue, 11 Jun 2024 14:21:16 +0200 Subject: [PATCH] feat(editor): Add move resources option to workflows and credentials on (#9654) --- cypress/composables/projects.ts | 41 +- cypress/e2e/39-projects.cy.ts | 147 ++++++ cypress/pages/credentials.ts | 2 + cypress/pages/workflows.ts | 2 + packages/editor-ui/src/api/credentials.ee.ts | 10 + packages/editor-ui/src/api/workflows.ee.ts | 10 + .../{__tests__ => }/CredentialCard.test.ts | 0 .../src/components/CredentialCard.vue | 277 ++++++------ packages/editor-ui/src/components/Modals.vue | 226 ++++----- .../ProjectMoveResourceConfirmModal.vue | 102 +++++ .../Projects/ProjectMoveResourceModal.vue | 108 +++++ .../components/Projects/ProjectSettings.vue | 4 +- .../{__tests__ => }/WorkflowCard.test.ts | 35 +- .../editor-ui/src/components/WorkflowCard.vue | 427 ++++++++++-------- .../layouts/ResourcesListLayout.vue | 2 + packages/editor-ui/src/constants.ts | 2 + .../src/{__tests__ => }/permissions.spec.ts | 4 + packages/editor-ui/src/permissions.ts | 8 +- .../src/plugins/i18n/locales/en.json | 13 + .../editor-ui/src/stores/projects.store.ts | 29 ++ packages/editor-ui/src/stores/ui.store.ts | 4 + .../editor-ui/src/views/CredentialsView.vue | 2 + 22 files changed, 960 insertions(+), 495 deletions(-) rename packages/editor-ui/src/components/{__tests__ => }/CredentialCard.test.ts (100%) create mode 100644 packages/editor-ui/src/components/Projects/ProjectMoveResourceConfirmModal.vue create mode 100644 packages/editor-ui/src/components/Projects/ProjectMoveResourceModal.vue rename packages/editor-ui/src/components/{__tests__ => }/WorkflowCard.test.ts (87%) rename packages/editor-ui/src/{__tests__ => }/permissions.spec.ts (96%) diff --git a/cypress/composables/projects.ts b/cypress/composables/projects.ts index dd25c3f20c..eb8f8b1eb4 100644 --- a/cypress/composables/projects.ts +++ b/cypress/composables/projects.ts @@ -1,3 +1,8 @@ +import { CredentialsModal, WorkflowPage } from '../pages'; + +const workflowPage = new WorkflowPage(); +const credentialsModal = new CredentialsModal(); + export const getHomeButton = () => cy.getByTestId('project-home-menu-item'); export const getMenuItems = () => cy.getByTestId('project-menu-item'); export const getAddProjectButton = () => cy.getByTestId('add-project-menu-item'); @@ -11,8 +16,42 @@ export const getProjectSettingsCancelButton = () => export const getProjectSettingsDeleteButton = () => cy.getByTestId('project-settings-delete-button'); export const getProjectMembersSelect = () => cy.getByTestId('project-members-select'); - export const addProjectMember = (email: string) => { getProjectMembersSelect().click(); getProjectMembersSelect().get('.el-select-dropdown__item').contains(email.toLowerCase()).click(); }; +export const getProjectNameInput = () => cy.get('#projectName'); +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) { + getAddProjectButton().should('be.visible').click(); + + getProjectNameInput() + .should('be.visible') + .should('be.focused') + .should('have.value', 'My project') + .clear() + .type(name); + getProjectSettingsSaveButton().click(); +} + +export function createWorkflow(fixtureKey: string, name: string) { + workflowPage.getters.workflowImportInput().selectFile(`fixtures/${fixtureKey}`, { force: true }); + workflowPage.actions.setWorkflowName(name); + workflowPage.getters.saveButton().should('contain', 'Saved'); + workflowPage.actions.zoomToFit(); +} + +export function createCredential(name: string) { + credentialsModal.getters.newCredentialModal().should('be.visible'); + credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); + credentialsModal.getters.newCredentialTypeOption('Notion API').click(); + credentialsModal.getters.newCredentialTypeButton().click(); + credentialsModal.getters.connectionParameter('Internal Integration Secret').type('1234567890'); + credentialsModal.actions.setName(name); + credentialsModal.actions.save(); + credentialsModal.actions.close(); +} diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts index 96e98a0031..6cec66d1cd 100644 --- a/cypress/e2e/39-projects.cy.ts +++ b/cypress/e2e/39-projects.cy.ts @@ -401,5 +401,152 @@ describe('Projects', () => { .first() .should('contain.text', 'Notion account personal project'); }); + + it('should move resources between projects', () => { + cy.signin(INSTANCE_OWNER); + cy.visit(workflowsPage.url); + + // Create a workflow and a credential in the Home project + workflowsPage.getters.workflowCards().should('not.have.length'); + workflowsPage.getters.newWorkflowButtonCard().click(); + projects.createWorkflow('Test_workflow_1.json', 'Workflow in Home project'); + + projects.getHomeButton().click(); + projects.getProjectTabCredentials().should('be.visible').click(); + credentialsPage.getters.emptyListCreateCredentialButton().click(); + projects.createCredential('Credential in Home project'); + + // Create a project and add a credential and a workflow to it + projects.createProject('Project 1'); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.emptyListCreateCredentialButton().click(); + projects.createCredential('Credential in Project 1'); + + projects.getProjectTabWorkflows().click(); + workflowsPage.getters.newWorkflowButtonCard().click(); + projects.createWorkflow('Test_workflow_1.json', 'Workflow in Project 1'); + + // Create another project and add a credential and a workflow to it + projects.createProject('Project 2'); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.emptyListCreateCredentialButton().click(); + projects.createCredential('Credential in Project 2'); + + projects.getProjectTabWorkflows().click(); + workflowsPage.getters.newWorkflowButtonCard().click(); + projects.createWorkflow('Test_workflow_1.json', 'Workflow in Project 2'); + + // Move the workflow owned by me from Home to Project 1 + projects.getHomeButton().click(); + workflowsPage.getters + .workflowCards() + .should('have.length', 3) + .filter(':contains("Owned by me")') + .should('exist'); + workflowsPage.getters.workflowCardActions('Workflow in Home project').click(); + workflowsPage.getters.workflowMoveButton().click(); + + projects + .getResourceMoveModal() + .should('be.visible') + .find('button:contains("Next")') + .should('be.disabled'); + projects.getProjectMoveSelect().click(); + getVisibleSelect() + .find('li') + .should('have.length', 2) + .first() + .should('contain.text', 'Project 1') + .click(); + projects.getResourceMoveModal().find('button:contains("Next")').click(); + + projects + .getResourceMoveConfirmModal() + .should('be.visible') + .find('button:contains("Confirm")') + .should('be.disabled'); + + projects + .getResourceMoveConfirmModal() + .find('input[type="checkbox"]') + .first() + .parents('label') + .click(); + projects + .getResourceMoveConfirmModal() + .find('button:contains("Confirm")') + .should('be.disabled'); + projects + .getResourceMoveConfirmModal() + .find('input[type="checkbox"]') + .last() + .parents('label') + .click(); + projects + .getResourceMoveConfirmModal() + .find('button:contains("Confirm")') + .should('not.be.disabled') + .click(); + + workflowsPage.getters + .workflowCards() + .should('have.length', 3) + .filter(':contains("Owned by me")') + .should('not.exist'); + + // 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(); + credentialsPage.getters.credentialMoveButton().click(); + + projects + .getResourceMoveModal() + .should('be.visible') + .find('button:contains("Next")') + .should('be.disabled'); + projects.getProjectMoveSelect().click(); + getVisibleSelect() + .find('li') + .should('have.length', 1) + .first() + .should('contain.text', 'Project 2') + .click(); + projects.getResourceMoveModal().find('button:contains("Next")').click(); + + projects + .getResourceMoveConfirmModal() + .should('be.visible') + .find('button:contains("Confirm")') + .should('be.disabled'); + + projects + .getResourceMoveConfirmModal() + .find('input[type="checkbox"]') + .first() + .parents('label') + .click(); + projects + .getResourceMoveConfirmModal() + .find('button:contains("Confirm")') + .should('be.disabled'); + projects + .getResourceMoveConfirmModal() + .find('input[type="checkbox"]') + .last() + .parents('label') + .click(); + projects + .getResourceMoveConfirmModal() + .find('button:contains("Confirm")') + .should('not.be.disabled') + .click(); + credentialsPage.getters.credentialCards().should('not.have.length'); + projects.getMenuItems().last().click(); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.credentialCards().should('have.length', 2); + }); }); }); diff --git a/cypress/pages/credentials.ts b/cypress/pages/credentials.ts index a356dc1421..9b20b48ec4 100644 --- a/cypress/pages/credentials.ts +++ b/cypress/pages/credentials.ts @@ -18,6 +18,8 @@ export class CredentialsPage extends BasePage { this.getters.credentialCard(credentialName).findChildByTestId('credential-card-actions'), credentialDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'), + credentialMoveButton: () => + cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Move'), sort: () => cy.getByTestId('resources-list-sort').first(), sortOption: (label: string) => cy.getByTestId('resources-list-sort-item').contains(label).first(), diff --git a/cypress/pages/workflows.ts b/cypress/pages/workflows.ts index 356b9ca5ec..5829ecb863 100644 --- a/cypress/pages/workflows.ts +++ b/cypress/pages/workflows.ts @@ -24,6 +24,8 @@ export class WorkflowsPage extends BasePage { this.getters.workflowCard(workflowName).findChildByTestId('workflow-card-actions'), workflowDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'), + workflowMoveButton: () => + cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Move'), workflowFilterButton: () => cy.getByTestId('resources-list-filters-trigger').filter(':visible'), workflowTagsDropdown: () => cy.getByTestId('tags-dropdown'), workflowTagItem: (tag: string) => cy.getByTestId('tag').contains(tag), diff --git a/packages/editor-ui/src/api/credentials.ee.ts b/packages/editor-ui/src/api/credentials.ee.ts index d6bc1cfe3c..590047a01c 100644 --- a/packages/editor-ui/src/api/credentials.ee.ts +++ b/packages/editor-ui/src/api/credentials.ee.ts @@ -14,3 +14,13 @@ export async function setCredentialSharedWith( data as unknown as IDataObject, ); } + +export async function moveCredentialToProject( + context: IRestApiContext, + id: string, + destinationProjectId: string, +): Promise { + return await makeRestApiRequest(context, 'PUT', `/credentials/${id}/transfer`, { + destinationProjectId, + }); +} diff --git a/packages/editor-ui/src/api/workflows.ee.ts b/packages/editor-ui/src/api/workflows.ee.ts index 19e99335b9..993d0d8725 100644 --- a/packages/editor-ui/src/api/workflows.ee.ts +++ b/packages/editor-ui/src/api/workflows.ee.ts @@ -14,3 +14,13 @@ export async function setWorkflowSharedWith( data as unknown as IDataObject, ); } + +export async function moveWorkflowToProject( + context: IRestApiContext, + id: string, + destinationProjectId: string, +): Promise { + return await makeRestApiRequest(context, 'PUT', `/workflows/${id}/transfer`, { + destinationProjectId, + }); +} diff --git a/packages/editor-ui/src/components/__tests__/CredentialCard.test.ts b/packages/editor-ui/src/components/CredentialCard.test.ts similarity index 100% rename from packages/editor-ui/src/components/__tests__/CredentialCard.test.ts rename to packages/editor-ui/src/components/CredentialCard.test.ts diff --git a/packages/editor-ui/src/components/CredentialCard.vue b/packages/editor-ui/src/components/CredentialCard.vue index 04425af7d3..bac96df489 100644 --- a/packages/editor-ui/src/components/CredentialCard.vue +++ b/packages/editor-ui/src/components/CredentialCard.vue @@ -1,3 +1,132 @@ + +