From 36108f82a1c1657c3959225d0635255668bf0af6 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Tue, 14 Feb 2023 16:13:22 +0200 Subject: [PATCH] feat(editor): Add correct credential owner contact details for readonly credentials (#5208) * feat: add correct credential owner contact details for readonly credentials * chore: remove unnecessary translation * fix: update credential owner name to be retrieved using usedCredentials * fix: correct credentialownername getter typing --- .../CredentialEdit/CredentialConfig.vue | 2 +- .../CredentialEdit/CredentialSharing.ee.vue | 2 +- .../src/components/NodeDetailsView.vue | 13 ++++--- .../editor-ui/src/components/NodeSettings.vue | 38 ++++++++++++++++--- .../src/plugins/i18n/locales/en.json | 2 +- packages/editor-ui/src/stores/credentials.ts | 14 +++++-- 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/packages/editor-ui/src/components/CredentialEdit/CredentialConfig.vue b/packages/editor-ui/src/components/CredentialEdit/CredentialConfig.vue index d3f132c4eb..e2255bd900 100644 --- a/packages/editor-ui/src/components/CredentialEdit/CredentialConfig.vue +++ b/packages/editor-ui/src/components/CredentialEdit/CredentialConfig.vue @@ -266,7 +266,7 @@ export default mixins(restApi).extend({ return (this.credentialType as ICredentialType).name; }, credentialOwnerName(): string { - return this.credentialsStore.getCredentialOwnerName(`${this.credentialId}`); + return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`); }, documentationUrl(): string { const type = this.credentialType as ICredentialType; diff --git a/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue b/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue index fc29c79688..10094bb05b 100644 --- a/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue +++ b/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue @@ -137,7 +137,7 @@ export default mixins(showMessage).extend({ ].concat(this.credentialData.sharedWith || []); }, credentialOwnerName(): string { - return this.credentialsStore.getCredentialOwnerName(`${this.credentialId}`); + return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`); }, }, methods: { diff --git a/packages/editor-ui/src/components/NodeDetailsView.vue b/packages/editor-ui/src/components/NodeDetailsView.vue index fcda16e656..07e27bbfba 100644 --- a/packages/editor-ui/src/components/NodeDetailsView.vue +++ b/packages/editor-ui/src/components/NodeDetailsView.vue @@ -93,7 +93,7 @@ :dragging="isDragging" :sessionId="sessionId" :nodeType="activeNodeType" - :hasForeignCredential="hasForeignCredential" + :foreignCredentials="foreignCredentials" :readOnly="readOnly" :blockUI="blockUi && showTriggerPanel" :executable="!readOnly" @@ -375,11 +375,11 @@ export default mixins( blockUi(): boolean { return this.isWorkflowRunning || this.isExecutionWaitingForWebhook; }, - hasForeignCredential(): boolean { + foreignCredentials(): string[] { const credentials = (this.activeNode || {}).credentials; const usedCredentials = this.workflowsStore.usedCredentials; - let hasForeignCredential = false; + const foreignCredentials: string[] = []; if ( credentials && this.settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing) @@ -390,12 +390,15 @@ export default mixins( usedCredentials[credential.id] && !usedCredentials[credential.id].currentUserHasAccess ) { - hasForeignCredential = true; + foreignCredentials.push(credential.id); } }); } - return hasForeignCredential; + return foreignCredentials; + }, + hasForeignCredential(): boolean { + return this.foreignCredentials.length > 0; }, }, watch: { diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index b1673a6f13..658d1076db 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -82,7 +82,7 @@ v-if="hasForeignCredential" :content=" $locale.baseText('nodeSettings.hasForeignCredential', { - interpolate: { owner: workflowOwnerName }, + interpolate: { owner: credentialOwnerName }, }) " /> @@ -166,7 +166,14 @@ import { NodeParameterValue, deepCopy, } from 'n8n-workflow'; -import { INodeUi, INodeUpdatePropertiesInformation, IUpdateInformation } from '@/Interface'; +import { + ICredentialsResponse, + INodeUi, + INodeUpdatePropertiesInformation, + IUpdateInformation, + IUsedCredential, + IUser, +} from '@/Interface'; import { COMMUNITY_NODES_INSTALLATION_DOCS_URL, @@ -197,6 +204,7 @@ import { useNodeTypesStore } from '@/stores/nodeTypes'; import { useHistoryStore } from '@/stores/history'; import { RenameNodeCommand } from '@/models/history'; import useWorkflowsEEStore from '@/stores/workflows.ee'; +import { useCredentialsStore } from '@/stores/credentials'; export default mixins(externalHooks, nodeHelpers).extend({ name: 'NodeSettings', @@ -215,6 +223,7 @@ export default mixins(externalHooks, nodeHelpers).extend({ useNodeTypesStore, useNDVStore, useUIStore, + useCredentialsStore, useWorkflowsStore, useWorkflowsEEStore, ), @@ -292,6 +301,25 @@ export default mixins(externalHooks, nodeHelpers).extend({ workflowOwnerName(): string { return this.workflowsEEStore.getWorkflowOwnerName(`${this.workflowsStore.workflowId}`); }, + hasForeignCredential(): boolean { + return this.foreignCredentials.length > 0; + }, + usedCredentials(): IUsedCredential[] { + return Object.values(this.workflowsStore.usedCredentials).filter((credential) => { + return Object.values(this.node?.credentials || []).find((nodeCredential) => { + return nodeCredential.id === credential.id; + }); + }); + }, + credentialOwnerName(): string { + const credential = this.usedCredentials + ? Object.values(this.usedCredentials).find((credential) => { + return credential.id === this.foreignCredentials[0]; + }) + : undefined; + + return this.credentialsStore.getCredentialOwnerName(credential); + }, }, props: { eventBus: {}, @@ -308,9 +336,9 @@ export default mixins(externalHooks, nodeHelpers).extend({ type: Boolean, default: false, }, - hasForeignCredential: { - type: Boolean, - default: false, + foreignCredentials: { + type: Array as PropType, + default: () => [], }, blockUI: { type: Boolean, diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 3e80d5d83c..20010cf8e3 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -785,7 +785,7 @@ "nodeSettings.useTheHttpRequestNode": "Use the HTTP Request node to make a custom API call. We'll take care of the {nodeTypeDisplayName} auth for you. Learn more", "nodeSettings.waitBetweenTries.description": "How long to wait between each attempt (in milliseconds)", "nodeSettings.waitBetweenTries.displayName": "Wait Between Tries (ms)", - "nodeSettings.hasForeignCredential": "To edit this node, either:
a) Ask credential owner to share the credential with you, or
b) Duplicate the node and add your own credential", + "nodeSettings.hasForeignCredential": "To edit this node, either:
a) Ask {owner} to share the credential with you, or
b) Duplicate the node and add your own credential", "nodeView.addNode": "Add node", "nodeView.addATriggerNodeFirst": "Add a Trigger Node first", "nodeView.addOrEnableTriggerNode": "Add or enable a Trigger node to execute the workflow", diff --git a/packages/editor-ui/src/stores/credentials.ts b/packages/editor-ui/src/stores/credentials.ts index 2874cd8bac..b3c071e2b8 100644 --- a/packages/editor-ui/src/stores/credentials.ts +++ b/packages/editor-ui/src/stores/credentials.ts @@ -1,4 +1,4 @@ -import { INodeUi } from './../Interface'; +import { INodeUi, IUsedCredential } from './../Interface'; import { createNewCredential, deleteCredential, @@ -177,13 +177,19 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, { }; }, getCredentialOwnerName() { - return (credentialId: string): string => { - const credential = this.getCredentialById(credentialId); - return credential && credential.ownedBy && credential.ownedBy.firstName + return (credential: ICredentialsResponse | IUsedCredential | undefined): string => { + return credential?.ownedBy?.firstName ? `${credential.ownedBy.firstName} ${credential.ownedBy.lastName} (${credential.ownedBy.email})` : i18n.baseText('credentialEdit.credentialSharing.info.sharee.fallback'); }; }, + getCredentialOwnerNameById() { + return (credentialId: string): string => { + const credential = this.getCredentialById(credentialId); + + return this.getCredentialOwnerName(credential); + }; + }, }, actions: { setCredentialTypes(credentialTypes: ICredentialType[]): void {