From c92765dcdb48789aa111ace29165a4b811fea710 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Wed, 5 Jun 2024 15:58:15 +0200 Subject: [PATCH] fix(editor): Filter credentials by project ID also for new workflow (#9615) --- cypress/e2e/39-projects.cy.ts | 145 +++++++++++++++++- packages/editor-ui/src/api/credentials.ts | 3 +- .../editor-ui/src/stores/credentials.store.ts | 6 +- packages/editor-ui/src/views/NodeView.vue | 21 ++- 4 files changed, 164 insertions(+), 11 deletions(-) diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts index 448817446..21685dd58 100644 --- a/cypress/e2e/39-projects.cy.ts +++ b/cypress/e2e/39-projects.cy.ts @@ -1,18 +1,21 @@ -import { INSTANCE_ADMIN, INSTANCE_MEMBERS } from '../constants'; +import { INSTANCE_ADMIN, INSTANCE_MEMBERS, INSTANCE_OWNER, MANUAL_TRIGGER_NODE_NAME, NOTION_NODE_NAME } from '../constants'; import { WorkflowsPage, WorkflowPage, CredentialsModal, CredentialsPage, WorkflowExecutionsTab, + NDV, } from '../pages'; import * as projects from '../composables/projects'; +import { getVisibleSelect } from '../utils'; const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); const credentialsPage = new CredentialsPage(); const credentialsModal = new CredentialsModal(); const executionsTab = new WorkflowExecutionsTab(); +const ndv = new NDV(); describe('Projects', () => { before(() => { @@ -150,9 +153,9 @@ describe('Projects', () => { projects.getHomeButton().click(); workflowsPage.getters.workflowCards().should('have.length', 2); - cy.intercept('GET', '/rest/credentials*').as('credentialsListFilterless'); + cy.intercept('GET', '/rest/credentials*').as('credentialsListUnfiltered'); projects.getProjectTabCredentials().click(); - cy.wait('@credentialsListFilterless').then((interception) => { + cy.wait('@credentialsListUnfiltered').then((interception) => { expect(interception.request.url).not.to.contain('filter'); }); @@ -228,4 +231,140 @@ describe('Projects', () => { projects.getAddProjectButton().should('not.exist'); projects.getMenuItems().should('not.exist'); }); + + describe('when starting from scratch', () => { + beforeEach(() => { + cy.resetDatabase(); + cy.enableFeature('sharing'); + cy.enableFeature('advancedPermissions'); + cy.enableFeature('projectRole:admin'); + cy.enableFeature('projectRole:editor'); + cy.changeQuota('maxTeamProjects', -1); + }); + + it('should filter credentials by project ID when creating new workflow or hard reloading an opened workflow', () => { + cy.signin(INSTANCE_OWNER); + cy.visit(workflowsPage.url); + + // Create a project and add a credential to it + cy.intercept('POST', '/rest/projects').as('projectCreate'); + projects.getAddProjectButton().should('contain', 'Add project').should('be.visible').click(); + cy.wait('@projectCreate'); + projects.getMenuItems().should('have.length', 1); + projects.getMenuItems().first().click(); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.credentialCards().should('not.have.length'); + + credentialsPage.getters.emptyListCreateCredentialButton().click(); + 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('Notion account project 1'); + + cy.intercept('POST', '/rest/credentials').as('credentialSave'); + credentialsModal.actions.save(); + cy.wait('@credentialSave').then((interception) => { + expect(interception.request.body).to.have.property('projectId'); + }); + credentialsModal.actions.close(); + + // Create another project and add a credential to it + projects.getAddProjectButton().click(); + cy.wait('@projectCreate'); + projects.getMenuItems().should('have.length', 2); + projects.getMenuItems().last().click(); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.credentialCards().should('not.have.length'); + + credentialsPage.getters.emptyListCreateCredentialButton().click(); + 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('Notion account project 2'); + + credentialsModal.actions.save(); + cy.wait('@credentialSave').then((interception) => { + expect(interception.request.body).to.have.property('projectId'); + }); + credentialsModal.actions.close(); + + // Create a credential in Home project + projects.getHomeButton().click(); + projects.getProjectTabCredentials().click(); + + credentialsPage.getters.credentialCards().should('have.length', 2); + + credentialsPage.getters.createCredentialButton().click(); + 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('Notion account personal project'); + + cy.intercept('POST', '/rest/credentials').as('credentialSave'); + credentialsModal.actions.save(); + cy.wait('@credentialSave') + credentialsModal.actions.close(); + + // Go to the first project and create a workflow + projects.getMenuItems().first().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); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account project 1'); + ndv.getters.backToCanvas().click(); + workflowPage.actions.saveWorkflowOnButtonClick(); + + cy.reload(); + workflowPage.getters.canvasNodeByName(NOTION_NODE_NAME).should('be.visible').dblclick(); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account project 1'); + ndv.getters.backToCanvas().click(); + + // Go to the second project and create a workflow + projects.getMenuItems().last().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); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account project 2'); + ndv.getters.backToCanvas().click(); + workflowPage.actions.saveWorkflowOnButtonClick(); + + cy.reload(); + workflowPage.getters.canvasNodeByName(NOTION_NODE_NAME).should('be.visible').dblclick(); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account project 2'); + ndv.getters.backToCanvas().click(); + + // Go to the Home project and create a workflow + projects.getHomeButton().click(); + projects.getProjectTabCredentials().click(); + credentialsPage.getters.credentialCards().should('have.length', 3); + + projects.getProjectTabWorkflows().click(); + workflowsPage.getters.workflowCards().should('have.length', 2); + workflowsPage.getters.createWorkflowButton().click(); + workflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); + workflowPage.actions.addNodeToCanvas(NOTION_NODE_NAME, true, true); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account personal project'); + ndv.getters.backToCanvas().click(); + workflowPage.actions.saveWorkflowOnButtonClick(); + + cy.reload(); + workflowPage.getters.canvasNodeByName(NOTION_NODE_NAME).should('be.visible').dblclick(); + workflowPage.getters.nodeCredentialsSelect().first().click(); + getVisibleSelect().find('li').should('have.length', 2).first().should('contain.text', 'Notion account personal project'); + + }); + }); }); diff --git a/packages/editor-ui/src/api/credentials.ts b/packages/editor-ui/src/api/credentials.ts index e396fb49b..5bc12bdb6 100644 --- a/packages/editor-ui/src/api/credentials.ts +++ b/packages/editor-ui/src/api/credentials.ts @@ -28,9 +28,10 @@ export async function getCredentialsNewName( export async function getAllCredentials( context: IRestApiContext, filter?: object, + includeScopes?: boolean, ): Promise { return await makeRestApiRequest(context, 'GET', '/credentials', { - includeScopes: true, + ...(includeScopes ? { includeScopes } : {}), ...(filter ? { filter } : {}), }); } diff --git a/packages/editor-ui/src/stores/credentials.store.ts b/packages/editor-ui/src/stores/credentials.store.ts index 2ce132193..0bba70e54 100644 --- a/packages/editor-ui/src/stores/credentials.store.ts +++ b/packages/editor-ui/src/stores/credentials.store.ts @@ -247,7 +247,10 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, { const credentialTypes = await getCredentialTypes(rootStore.getBaseUrl); this.setCredentialTypes(credentialTypes); }, - async fetchAllCredentials(projectId?: string): Promise { + async fetchAllCredentials( + projectId?: string, + includeScopes = true, + ): Promise { const rootStore = useRootStore(); const filter = { @@ -257,6 +260,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, { const credentials = await getAllCredentials( rootStore.getRestApiContext, isEmpty(filter) ? undefined : filter, + includeScopes, ); this.setCredentials(credentials); return credentials; diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index a7cda9067..fede51abc 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -3794,7 +3794,6 @@ export default defineComponent({ return; } } - await this.loadCredentials(); // Load a workflow let workflowId = null as string | null; if (this.$route.params.name) { @@ -3838,6 +3837,7 @@ export default defineComponent({ await this.newWorkflow(); } } + await this.loadCredentials(); this.historyStore.reset(); this.uiStore.nodeViewInitialized = true; document.addEventListener('keydown', this.keyDown); @@ -4789,11 +4789,20 @@ export default defineComponent({ }, async loadCredentials(): Promise { const workflow = this.workflowsStore.getWorkflowById(this.currentWorkflow); - const projectId = - workflow?.homeProject?.type === ProjectTypes.Personal - ? this.projectsStore.personalProject?.id - : workflow?.homeProject?.id; - await this.credentialsStore.fetchAllCredentials(projectId); + let projectId: string | undefined; + if (workflow) { + projectId = + workflow.homeProject?.type === ProjectTypes.Personal + ? this.projectsStore.personalProject?.id + : workflow?.homeProject?.id ?? this.projectsStore.currentProjectId; + } else { + const queryParam = + typeof this.$route.query?.projectId === 'string' + ? this.$route.query?.projectId + : undefined; + projectId = queryParam ?? this.projectsStore.personalProject?.id; + } + await this.credentialsStore.fetchAllCredentials(projectId, false); }, async loadVariables(): Promise { await this.environmentsStore.fetchAllVariables();