diff --git a/packages/editor-ui/src/components/NodeCredentials.vue b/packages/editor-ui/src/components/NodeCredentials.vue index ad3f93b04e..8af8d36fc2 100644 --- a/packages/editor-ui/src/components/NodeCredentials.vue +++ b/packages/editor-ui/src/components/NodeCredentials.vue @@ -123,7 +123,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend( }; }, mounted() { - this.listenForNewCredentials(); + this.listenForCredentialUpdates(); }, computed: { ...mapStores( @@ -133,9 +133,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend( useUsersStore, useWorkflowsStore, ), - allCredentialsByType(): { [type: string]: ICredentialsResponse[] } { - return this.credentialsStore.allCredentialsByType; - }, currentUser(): IUser { return this.usersStore.currentUser || ({} as IUser); }, @@ -182,13 +179,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend( methods: { getCredentialOptions(type: string): ICredentialsResponse[] { - return (this.allCredentialsByType as Record)[type].filter( - (credential) => { - const permissions = getCredentialPermissions(this.currentUser, credential); - - return permissions.use; - }, - ); + return this.credentialsStore.allUsableCredentialsByType[type]; }, getSelectedId(type: string) { if (this.isCredentialExisting(type)) { @@ -221,41 +212,71 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend( return styles; }, - // TODO: Investigate if this can be solved using only the store data (storing selected flag in credentials objects, ...) - listenForNewCredentials() { - // Listen for credentials store changes so credential selection can be updated if creds are changed from the modal - this.credentialsStore.$subscribe((mutation, state) => { + // Listen for credentials store changes so credential selection can be updated if creds are changed from the modal + listenForCredentialUpdates() { + const getCounts = () => { + return Object.keys(this.credentialsStore.allUsableCredentialsByType).reduce( + (counts: { [key: string]: number }, key: string) => { + counts[key] = this.credentialsStore.allUsableCredentialsByType[key].length; + return counts; + }, + {}, + ); + }; + + let previousCredentialCounts = getCounts(); + const onCredentialMutation = () => { // This data pro stores credential type that the component is currently interested in const credentialType = this.subscribedToCredentialType; - let credentialsOfType = this.credentialsStore.allCredentialsByType[credentialType]; - - if (credentialsOfType) { - credentialsOfType = credentialsOfType.sort((a, b) => (a.id < b.id ? -1 : 1)); - if (credentialsOfType.length > 0) { - // If nothing has been selected previously, select the first one (newly added) - if (!this.selected[credentialType]) { - this.onCredentialSelected(credentialType, credentialsOfType[0].id); - } else { - // Else, check id currently selected cred has been updated - const newSelected = credentialsOfType.find( - (cred) => cred.id === this.selected[credentialType].id, - ); - // If it has changed, select it - if (newSelected && newSelected.name !== this.selected[credentialType].name) { - this.onCredentialSelected(credentialType, newSelected.id); - } else { - // Else select the last cred with that type since selected has been deleted or a new one has been added - this.onCredentialSelected( - credentialType, - credentialsOfType[credentialsOfType.length - 1].id, - ); - } - } - } + if (!credentialType) { + return; } - this.subscribedToCredentialType = ''; + + let credentialsOfType = [ + ...(this.credentialsStore.allUsableCredentialsByType[credentialType] || []), + ]; + // all credentials were deleted + if (credentialsOfType.length === 0) { + this.clearSelectedCredential(credentialType); + return; + } + + credentialsOfType = credentialsOfType.sort((a, b) => (a.id < b.id ? -1 : 1)); + const previousCredsOfType = previousCredentialCounts[credentialType] || 0; + const current = this.selected[credentialType]; + + // new credential was added + if (credentialsOfType.length > previousCredsOfType || !current) { + this.onCredentialSelected( + credentialType, + credentialsOfType[credentialsOfType.length - 1].id, + ); + return; + } + + const matchingCredential = credentialsOfType.find((cred) => cred.id === current.id); + // credential was deleted, select last one added to replace with + if (!matchingCredential) { + this.onCredentialSelected( + credentialType, + credentialsOfType[credentialsOfType.length - 1].id, + ); + return; + } + + // credential was updated + if (matchingCredential.name !== current.name) { + // credential name was changed, update it + this.onCredentialSelected(credentialType, current.id); + } + }; + + this.credentialsStore.$subscribe((mutation, state) => { + onCredentialMutation(); + previousCredentialCounts = getCounts(); }); }, + clearSelectedCredential(credentialType: string) { const node: INodeUi = this.node; @@ -277,7 +298,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend( onCredentialSelected(credentialType: string, credentialId: string | null | undefined) { if (credentialId === this.NEW_CREDENTIALS_TEXT) { - // this.listenForNewCredentials(credentialType); this.subscribedToCredentialType = credentialType; } if (!credentialId || credentialId === this.NEW_CREDENTIALS_TEXT) { diff --git a/packages/editor-ui/src/stores/credentials.ts b/packages/editor-ui/src/stores/credentials.ts index 6944f886bf..a8b7a64d54 100644 --- a/packages/editor-ui/src/stores/credentials.ts +++ b/packages/editor-ui/src/stores/credentials.ts @@ -72,6 +72,22 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, { {}, ); }, + allUsableCredentialsByType(): { [type: string]: ICredentialsResponse[] } { + const credentials = this.allCredentials; + const types = this.allCredentialTypes; + const usersStore = useUsersStore(); + + return types.reduce( + (accu: { [type: string]: ICredentialsResponse[] }, type: ICredentialType) => { + accu[type.name] = credentials.filter((cred: ICredentialsResponse) => { + return cred.type === type.name && usersStore.isResourceAccessible(cred); + }); + + return accu; + }, + {}, + ); + }, getCredentialTypeByName() { return (type: string): ICredentialType => this.credentialTypes[type]; }, @@ -89,6 +105,11 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, { return this.allCredentialsByType[credentialType] || []; }; }, + getUsableCredentialByType() { + return (credentialType: string): ICredentialsResponse[] => { + return this.allUsableCredentialsByType[credentialType] || []; + }; + }, getNodesWithAccess() { return (credentialTypeName: string) => { const nodeTypesStore = useNodeTypesStore(); diff --git a/packages/editor-ui/src/stores/users.ts b/packages/editor-ui/src/stores/users.ts index 455b1d7f6e..c468fff114 100644 --- a/packages/editor-ui/src/stores/users.ts +++ b/packages/editor-ui/src/stores/users.ts @@ -18,14 +18,16 @@ import { validatePasswordToken, validateSignupToken, } from '@/api/users'; -import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants'; +import { EnterpriseEditionFeature, PERSONALIZATION_MODAL_KEY, STORES } from '@/constants'; import { + ICredentialsResponse, IInviteResponse, IPersonalizationLatestVersion, IUser, IUserResponse, IUsersState, } from '@/Interface'; +import { getCredentialPermissions } from '@/permissions'; import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils'; import { defineStore } from 'pinia'; import Vue from 'vue'; @@ -90,6 +92,13 @@ export const useUsersStore = defineStore(STORES.USERS, { } return getPersonalizedNodeTypes(answers); }, + isResourceAccessible() { + return (resource: ICredentialsResponse): boolean => { + const permissions = getCredentialPermissions(this.currentUser, resource); + + return permissions.use; + }; + }, }, actions: { addUsers(users: IUserResponse[]) { diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 022bf5928e..72820e4803 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -1714,7 +1714,7 @@ export default mixins( const credentialPerType = nodeTypeData.credentials && nodeTypeData.credentials - .map((type) => this.credentialsStore.getCredentialsByType(type.name)) + .map((type) => this.credentialsStore.getUsableCredentialByType(type.name)) .flat(); if (credentialPerType && credentialPerType.length === 1) {