2019-06-23 03:35:23 -07:00
< template >
2021-10-27 12:55:37 -07:00
< div v-if ="credentialTypesNodeDescriptionDisplayed.length" :class="$style.container" >
< div v-for ="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed" :key="credentialTypeDescription.name" >
< n8n -input -label
: label = "`Credential for ${credentialTypeNames[credentialTypeDescription.name]}`"
: bold = "false"
size = "small"
: set = "issues = getIssues(credentialTypeDescription.name)"
>
< div v-if ="isReadOnly" >
< n8n -input disabled : value = "selected && selected[credentialTypeDescription.name] && selected[credentialTypeDescription.name].name" size = "small" / >
< / div >
< div : class = "issues.length ? $style.hasIssues : $style.input" v -else >
< n8n -select :value ="getSelectedId(credentialTypeDescription.name)" @ change = "(value) => onCredentialSelected(credentialTypeDescription.name, value)" placeholder = "Select Credential" size = "small" >
< n8n -option
v - for = "(item) in credentialOptions[credentialTypeDescription.name]"
: key = "item.id"
: label = "item.name"
: value = "item.id" >
< / n 8 n - o p t i o n >
< n8n -option
: key = "NEW_CREDENTIALS_TEXT"
: value = "NEW_CREDENTIALS_TEXT"
: label = "NEW_CREDENTIALS_TEXT"
>
< / n 8 n - o p t i o n >
< / n 8 n - s e l e c t >
< div :class ="$style.warning" v-if ="issues.length" >
2021-08-29 04:36:17 -07:00
< n8n -tooltip placement = "top" >
2021-10-27 12:55:37 -07:00
< div slot = "content" v-html ="'Issues:<br /> - ' + issues.join('<br /> - ')" > < / div >
2021-08-29 04:36:17 -07:00
< font -awesome -icon icon = "exclamation-triangle" / >
< / n 8 n - t o o l t i p >
2019-06-23 03:35:23 -07:00
< / div >
2021-10-27 12:55:37 -07:00
< div :class ="$style.edit" v-if ="selected[credentialTypeDescription.name] && isCredentialExisting(credentialTypeDescription.name)" >
< font -awesome -icon icon = "pen" @click ="editCredential(credentialTypeDescription.name)" class = "clickable" title = "Update Credentials" / >
< / div >
< / div >
< / n 8 n - i n p u t - l a b e l >
2019-06-23 03:35:23 -07:00
< / div >
< / div >
< / template >
< script lang = "ts" >
import { restApi } from '@/components/mixins/restApi' ;
import {
2021-10-13 15:21:00 -07:00
ICredentialsResponse ,
2019-06-23 03:35:23 -07:00
INodeUi ,
INodeUpdatePropertiesInformation ,
} from '@/Interface' ;
import {
ICredentialType ,
INodeCredentialDescription ,
2021-10-13 15:21:00 -07:00
INodeCredentialsDetails ,
2019-06-23 03:35:23 -07:00
INodeTypeDescription ,
} from 'n8n-workflow' ;
import { genericHelpers } from '@/components/mixins/genericHelpers' ;
import { nodeHelpers } from '@/components/mixins/nodeHelpers' ;
import { showMessage } from '@/components/mixins/showMessage' ;
2021-09-11 01:15:36 -07:00
import { mapGetters } from "vuex" ;
2019-06-23 03:35:23 -07:00
import mixins from 'vue-typed-mixins' ;
2021-09-11 01:15:36 -07:00
const NEW _CREDENTIALS _TEXT = '- Create New -' ;
2019-06-23 03:35:23 -07:00
export default mixins (
genericHelpers ,
nodeHelpers ,
restApi ,
showMessage ,
) . extend ( {
name : 'NodeCredentials' ,
props : [
'node' , // INodeUi
] ,
2021-09-11 01:15:36 -07:00
data ( ) {
return {
NEW _CREDENTIALS _TEXT ,
newCredentialUnsubscribe : null as null | ( ( ) => void ) ,
} ;
2019-06-23 03:35:23 -07:00
} ,
computed : {
2021-09-11 01:15:36 -07:00
... mapGetters ( 'credentials' , {
credentialOptions : 'allCredentialsByType' ,
} ) ,
2019-06-23 03:35:23 -07:00
credentialTypesNode ( ) : string [ ] {
return this . credentialTypesNodeDescription
. map ( ( credentialTypeDescription ) => credentialTypeDescription . name ) ;
} ,
credentialTypesNodeDescriptionDisplayed ( ) : INodeCredentialDescription [ ] {
return this . credentialTypesNodeDescription
. filter ( ( credentialTypeDescription ) => {
return this . displayCredentials ( credentialTypeDescription ) ;
} ) ;
} ,
credentialTypesNodeDescription ( ) : INodeCredentialDescription [ ] {
const node = this . node as INodeUi ;
const activeNodeType = this . $store . getters . nodeType ( node . type ) as INodeTypeDescription ;
if ( activeNodeType && activeNodeType . credentials ) {
return activeNodeType . credentials ;
}
return [ ] ;
} ,
credentialTypeNames ( ) {
const returnData : {
[ key : string ] : string ;
} = { } ;
let credentialType : ICredentialType | null ;
for ( const credentialTypeName of this . credentialTypesNode ) {
2021-09-11 01:15:36 -07:00
credentialType = this . $store . getters [ 'credentials/getCredentialTypeByName' ] ( credentialTypeName ) ;
2019-06-23 03:35:23 -07:00
returnData [ credentialTypeName ] = credentialType !== null ? credentialType . displayName : credentialTypeName ;
}
return returnData ;
} ,
2021-10-13 15:21:00 -07:00
selected ( ) : { [ type : string ] : INodeCredentialsDetails } {
2021-09-11 01:15:36 -07:00
return this . node . credentials || { } ;
2019-06-23 03:35:23 -07:00
} ,
} ,
methods : {
2021-10-13 15:21:00 -07:00
getSelectedId ( type : string ) {
if ( this . isCredentialExisting ( type ) ) {
return this . selected [ type ] . id ;
}
return undefined ;
} ,
2019-06-23 03:35:23 -07:00
credentialInputWrapperStyle ( credentialType : string ) {
let deductWidth = 0 ;
const styles = {
width : '100%' ,
} ;
if ( this . getIssues ( credentialType ) . length ) {
deductWidth += 20 ;
}
if ( deductWidth !== 0 ) {
styles . width = ` calc(100% - ${ deductWidth } px) ` ;
}
return styles ;
} ,
2021-09-11 01:15:36 -07:00
listenForNewCredentials ( credentialType : string ) {
this . stopListeningForNewCredentials ( ) ;
this . newCredentialUnsubscribe = this . $store . subscribe ( ( mutation , state ) => {
if ( mutation . type === 'credentials/upsertCredential' || mutation . type === 'credentials/enableOAuthCredential' ) {
2021-10-13 15:21:00 -07:00
this . onCredentialSelected ( credentialType , mutation . payload . id ) ;
2021-09-11 01:15:36 -07:00
}
if ( mutation . type === 'credentials/deleteCredential' ) {
2021-10-13 15:21:00 -07:00
this . clearSelectedCredential ( credentialType ) ;
2021-09-11 01:15:36 -07:00
this . stopListeningForNewCredentials ( ) ;
}
} ) ;
} ,
stopListeningForNewCredentials ( ) {
if ( this . newCredentialUnsubscribe ) {
this . newCredentialUnsubscribe ( ) ;
2019-06-23 03:35:23 -07:00
}
2021-09-11 01:15:36 -07:00
} ,
2019-06-23 03:35:23 -07:00
2021-10-13 15:21:00 -07:00
clearSelectedCredential ( credentialType : string ) {
const node : INodeUi = this . node ;
const credentials = {
... ( node . credentials || { } ) ,
} ;
delete credentials [ credentialType ] ;
const updateInformation : INodeUpdatePropertiesInformation = {
name : this . node . name ,
properties : {
credentials ,
} ,
} ;
this . $emit ( 'credentialSelected' , updateInformation ) ;
} ,
onCredentialSelected ( credentialType : string , credentialId : string | null | undefined ) {
if ( credentialId === NEW _CREDENTIALS _TEXT ) {
2021-09-11 01:15:36 -07:00
this . listenForNewCredentials ( credentialType ) ;
this . $store . dispatch ( 'ui/openNewCredential' , { type : credentialType } ) ;
2021-10-18 20:57:49 -07:00
this . $telemetry . track ( 'User opened Credential modal' , { credential _type : credentialType , source : 'node' , new _credential : true , workflow _id : this . $store . getters . workflowId } ) ;
2021-10-13 15:21:00 -07:00
return ;
2021-09-11 01:15:36 -07:00
}
2021-10-13 15:21:00 -07:00
2021-10-18 20:57:49 -07:00
this . $telemetry . track ( 'User selected credential from node modal' , { credential _type : credentialType , workflow _id : this . $store . getters . workflowId } ) ;
2021-10-13 15:21:00 -07:00
const selectedCredentials = this . $store . getters [ 'credentials/getCredentialById' ] ( credentialId ) ;
const oldCredentials = this . node . credentials && this . node . credentials [ credentialType ] ? this . node . credentials [ credentialType ] : { } ;
2021-10-18 20:57:49 -07:00
const selected = { id : selectedCredentials . id , name : selectedCredentials . name } ;
2021-10-13 15:21:00 -07:00
// if credentials has been string or neither id matched nor name matched uniquely
if ( oldCredentials . id === null || ( oldCredentials . id && ! this . $store . getters [ 'credentials/getCredentialByIdAndType' ] ( oldCredentials . id , credentialType ) ) ) {
// update all nodes in the workflow with the same old/invalid credentials
this . $store . commit ( 'replaceInvalidWorkflowCredentials' , {
credentials : selected ,
invalid : oldCredentials ,
type : credentialType ,
} ) ;
this . updateNodesCredentialsIssues ( ) ;
this . $showMessage ( {
title : 'Node credentials updated' ,
message : ` Nodes that used credentials " ${ oldCredentials . name } " have been updated to use " ${ selected . name } " ` ,
type : 'success' ,
} ) ;
2021-09-11 01:15:36 -07:00
}
const node : INodeUi = this . node ;
const credentials = {
... ( node . credentials || { } ) ,
[ credentialType ] : selected ,
} ;
2019-06-23 03:35:23 -07:00
const updateInformation : INodeUpdatePropertiesInformation = {
2021-09-11 01:15:36 -07:00
name : this . node . name ,
2019-06-23 03:35:23 -07:00
properties : {
2021-09-11 01:15:36 -07:00
credentials ,
2019-06-23 03:35:23 -07:00
} ,
} ;
this . $emit ( 'credentialSelected' , updateInformation ) ;
} ,
2021-09-11 01:15:36 -07:00
2019-06-23 03:35:23 -07:00
displayCredentials ( credentialTypeDescription : INodeCredentialDescription ) : boolean {
if ( credentialTypeDescription . displayOptions === undefined ) {
// If it is not defined no need to do a proper check
return true ;
}
return this . displayParameter ( this . node . parameters , credentialTypeDescription , '' ) ;
} ,
2021-09-11 01:15:36 -07:00
2019-06-23 03:35:23 -07:00
getIssues ( credentialTypeName : string ) : string [ ] {
const node = this . node as INodeUi ;
if ( node . issues === undefined || node . issues . credentials === undefined ) {
return [ ] ;
}
if ( ! node . issues . credentials . hasOwnProperty ( credentialTypeName ) ) {
return [ ] ;
}
return node . issues . credentials [ credentialTypeName ] ;
} ,
2021-10-13 15:21:00 -07:00
isCredentialExisting ( credentialType : string ) : boolean {
if ( ! this . node . credentials || ! this . node . credentials [ credentialType ] || ! this . node . credentials [ credentialType ] . id ) {
return false ;
}
const { id } = this . node . credentials [ credentialType ] ;
2021-09-11 01:15:36 -07:00
const options = this . credentialOptions [ credentialType ] ;
2019-06-23 03:35:23 -07:00
2021-10-13 15:21:00 -07:00
return ! ! options . find ( ( option : ICredentialsResponse ) => option . id === id ) ;
2019-06-23 03:35:23 -07:00
} ,
2021-09-11 01:15:36 -07:00
editCredential ( credentialType : string ) : void {
2021-10-13 15:21:00 -07:00
const { id } = this . node . credentials [ credentialType ] ;
this . $store . dispatch ( 'ui/openExisitngCredential' , { id } ) ;
2019-06-23 03:35:23 -07:00
2021-10-18 20:57:49 -07:00
this . $telemetry . track ( 'User opened Credential modal' , { credential _type : credentialType , source : 'node' , new _credential : false , workflow _id : this . $store . getters . workflowId } ) ;
2021-09-11 01:15:36 -07:00
this . listenForNewCredentials ( credentialType ) ;
2019-06-23 03:35:23 -07:00
} ,
} ,
2021-09-11 01:15:36 -07:00
beforeDestroy ( ) {
this . stopListeningForNewCredentials ( ) ;
2019-06-23 03:35:23 -07:00
} ,
} ) ;
< / script >
2021-10-27 12:55:37 -07:00
< style lang = "scss" module >
. container {
margin : var ( -- spacing - xs ) 0 ;
2019-06-23 03:35:23 -07:00
2021-10-27 12:55:37 -07:00
> * {
margin - bottom : var ( -- spacing - xs ) ;
2019-06-23 03:35:23 -07:00
}
2021-10-27 12:55:37 -07:00
}
2019-06-23 03:35:23 -07:00
2021-10-27 12:55:37 -07:00
. warning {
min - width : 20 px ;
margin - left : 5 px ;
color : # ff8080 ;
font - size : var ( -- font - size - s ) ;
}
2021-08-29 04:36:17 -07:00
2021-10-27 12:55:37 -07:00
. edit {
display : flex ;
justify - content : center ;
align - items : center ;
color : var ( -- color - text - base ) ;
min - width : 20 px ;
margin - left : 5 px ;
font - size : var ( -- font - size - s ) ;
}
2021-10-13 15:21:00 -07:00
2021-10-27 12:55:37 -07:00
. input {
display : flex ;
align - items : center ;
2019-06-23 03:35:23 -07:00
}
2021-10-27 12:55:37 -07:00
. hasIssues {
composes : input ;
-- input - border - color : var ( -- color - danger ) ;
}
2019-06-23 03:35:23 -07:00
< / style >