2019-06-23 03:35:23 -07:00
|
|
|
<template>
|
|
|
|
<div v-if="credentialTypesNodeDescriptionDisplayed.length" class="node-credentials">
|
2021-01-24 04:33:57 -08:00
|
|
|
<credentials-edit :dialogVisible="credentialNewDialogVisible" :editCredentials="editCredentials" :setCredentialType="addType" :nodesInit="nodesInit" :node="node" @closeDialog="closeCredentialNewDialog" @credentialsCreated="credentialsCreated" @credentialsUpdated="credentialsUpdated"></credentials-edit>
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
<div class="headline">
|
|
|
|
Credentials
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed" :key="credentialTypeDescription.name" class="credential-data">
|
2021-08-29 04:36:17 -07:00
|
|
|
<el-row v-if="displayCredentials(credentialTypeDescription)" class="credential-parameter-wrapper">
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
<el-col :span="10" class="parameter-name">
|
|
|
|
{{credentialTypeNames[credentialTypeDescription.name]}}:
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="12" class="parameter-value" :class="getIssues(credentialTypeDescription.name).length?'has-issues':''">
|
2021-08-29 04:36:17 -07:00
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
<div :style="credentialInputWrapperStyle(credentialTypeDescription.name)">
|
2021-08-29 04:36:17 -07:00
|
|
|
<n8n-select v-model="credentials[credentialTypeDescription.name]" :disabled="isReadOnly" @change="credentialSelected(credentialTypeDescription.name)" placeholder="Select Credential" size="small">
|
|
|
|
<n8n-option
|
2019-06-23 03:35:23 -07:00
|
|
|
v-for="(item, index) in credentialOptions[credentialTypeDescription.name]"
|
|
|
|
:key="item.name + '_' + index"
|
|
|
|
:label="item.name"
|
|
|
|
:value="item.name">
|
2021-08-29 04:36:17 -07:00
|
|
|
</n8n-option>
|
|
|
|
</n8n-select>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="credential-issues">
|
|
|
|
<n8n-tooltip placement="top" >
|
|
|
|
<div slot="content" v-html="'Issues:<br /> - ' + getIssues(credentialTypeDescription.name).join('<br /> - ')"></div>
|
|
|
|
<font-awesome-icon icon="exclamation-triangle" />
|
|
|
|
</n8n-tooltip>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
2021-08-29 04:36:17 -07:00
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
</el-col>
|
2021-08-29 04:36:17 -07:00
|
|
|
<el-col :span="2" class="parameter-value credential-action">
|
2019-06-23 03:35:23 -07:00
|
|
|
<font-awesome-icon v-if="credentials[credentialTypeDescription.name]" icon="pen" @click="updateCredentials(credentialTypeDescription.name)" class="update-credentials clickable" title="Update Credentials" />
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
</el-row>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
import Vue from 'vue';
|
|
|
|
|
|
|
|
import { restApi } from '@/components/mixins/restApi';
|
|
|
|
import {
|
2020-02-08 16:14:28 -08:00
|
|
|
ICredentialsCreatedEvent,
|
2019-06-23 03:35:23 -07:00
|
|
|
ICredentialsResponse,
|
|
|
|
INodeUi,
|
|
|
|
INodeUpdatePropertiesInformation,
|
|
|
|
} from '@/Interface';
|
|
|
|
import {
|
|
|
|
ICredentialType,
|
|
|
|
INodeCredentialDescription,
|
|
|
|
INodeTypeDescription,
|
|
|
|
} from 'n8n-workflow';
|
|
|
|
|
|
|
|
import CredentialsEdit from '@/components/CredentialsEdit.vue';
|
|
|
|
import ParameterInput from '@/components/ParameterInput.vue';
|
|
|
|
|
|
|
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
|
|
|
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
|
|
|
import { showMessage } from '@/components/mixins/showMessage';
|
|
|
|
|
|
|
|
import mixins from 'vue-typed-mixins';
|
|
|
|
|
|
|
|
export default mixins(
|
|
|
|
genericHelpers,
|
|
|
|
nodeHelpers,
|
|
|
|
restApi,
|
|
|
|
showMessage,
|
|
|
|
).extend({
|
|
|
|
name: 'NodeCredentials',
|
|
|
|
props: [
|
|
|
|
'node', // INodeUi
|
|
|
|
],
|
|
|
|
components: {
|
|
|
|
CredentialsEdit,
|
|
|
|
ParameterInput,
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
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) {
|
|
|
|
credentialType = this.$store.getters.credentialType(credentialTypeName);
|
|
|
|
returnData[credentialTypeName] = credentialType !== null ? credentialType.displayName : credentialTypeName;
|
|
|
|
}
|
|
|
|
return returnData;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
addType: undefined as string | undefined,
|
|
|
|
credentialNewDialogVisible: false,
|
|
|
|
credentialOptions: {} as { [key: string]: ICredentialsResponse[]; },
|
|
|
|
credentials: {} as {
|
|
|
|
[key: string]: string | undefined
|
|
|
|
},
|
|
|
|
editCredentials: null as object | null, // Credentials filter
|
|
|
|
newCredentialText: '- Create New -',
|
|
|
|
nodesInit: undefined as string[] | undefined,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
node () {
|
|
|
|
this.init();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
closeCredentialNewDialog () {
|
|
|
|
this.credentialNewDialogVisible = false;
|
|
|
|
},
|
2020-02-08 16:14:28 -08:00
|
|
|
async credentialsCreated (eventData: ICredentialsCreatedEvent) {
|
2020-02-08 18:46:41 -08:00
|
|
|
await this.credentialsUpdated(eventData);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2020-02-08 16:14:28 -08:00
|
|
|
credentialsUpdated (eventData: ICredentialsCreatedEvent) {
|
|
|
|
if (!this.credentialTypesNode.includes(eventData.data.type)) {
|
2019-06-23 03:35:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.init();
|
2020-02-08 16:14:28 -08:00
|
|
|
Vue.set(this.credentials, eventData.data.type, eventData.data.name);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
// Makes sure that it does also get set correctly on the node not just the UI
|
2020-02-08 16:14:28 -08:00
|
|
|
this.credentialSelected(eventData.data.type);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2020-02-08 16:14:28 -08:00
|
|
|
if (eventData.options.closeDialog === true) {
|
|
|
|
this.closeCredentialNewDialog();
|
|
|
|
}
|
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;
|
|
|
|
},
|
|
|
|
credentialSelected (credentialType: string) {
|
|
|
|
const credential = this.credentials[credentialType];
|
|
|
|
if (credential === this.newCredentialText) {
|
|
|
|
// New credentials should be created
|
|
|
|
this.addType = credentialType;
|
|
|
|
this.editCredentials = null;
|
|
|
|
this.nodesInit = [ (this.node as INodeUi).type ];
|
|
|
|
this.credentialNewDialogVisible = true;
|
|
|
|
|
|
|
|
this.credentials[credentialType] = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const node = this.node as INodeUi;
|
|
|
|
|
|
|
|
const updateInformation: INodeUpdatePropertiesInformation = {
|
|
|
|
name: node.name,
|
|
|
|
properties: {
|
|
|
|
credentials: JSON.parse(JSON.stringify(this.credentials)),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
this.$emit('credentialSelected', updateInformation);
|
|
|
|
},
|
|
|
|
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, '');
|
|
|
|
},
|
|
|
|
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];
|
|
|
|
},
|
|
|
|
updateCredentials (credentialType: string): void {
|
|
|
|
const name = this.credentials[credentialType];
|
|
|
|
const credentialData = this.credentialOptions[credentialType].find((optionData: ICredentialsResponse) => optionData.name === name);
|
|
|
|
if (credentialData === undefined) {
|
|
|
|
this.$showMessage({
|
|
|
|
title: 'Credentials not found',
|
|
|
|
message: `The credentials named "${name}" of type "${credentialType}" could not be found!`,
|
|
|
|
type: 'error',
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const editCredentials = {
|
|
|
|
id: credentialData.id,
|
|
|
|
name,
|
|
|
|
type: credentialType,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.editCredentials = editCredentials;
|
|
|
|
this.addType = credentialType;
|
|
|
|
this.credentialNewDialogVisible = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
init () {
|
|
|
|
const node = this.node as INodeUi;
|
|
|
|
|
|
|
|
const newOption = {
|
|
|
|
name: this.newCredentialText,
|
|
|
|
};
|
|
|
|
|
|
|
|
let options = [];
|
|
|
|
|
|
|
|
// Get the available credentials for each type
|
|
|
|
for (const credentialType of this.credentialTypesNode) {
|
|
|
|
options = this.$store.getters.credentialsByType(credentialType);
|
|
|
|
options.push(newOption as ICredentialsResponse);
|
|
|
|
Vue.set(this.credentialOptions, credentialType, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the current node credentials
|
|
|
|
if (node.credentials) {
|
|
|
|
Vue.set(this, 'credentials', JSON.parse(JSON.stringify(node.credentials)));
|
|
|
|
} else {
|
|
|
|
Vue.set(this, 'credentials', {});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
mounted () {
|
|
|
|
this.init();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
|
|
|
.node-credentials {
|
|
|
|
padding-bottom: 1em;
|
|
|
|
margin: 0.5em;
|
|
|
|
border-bottom: 1px solid #ccc;
|
|
|
|
|
|
|
|
.credential-issues {
|
|
|
|
display: none;
|
|
|
|
width: 20px;
|
|
|
|
text-align: right;
|
|
|
|
float: right;
|
|
|
|
color: #ff8080;
|
|
|
|
font-size: 1.2em;
|
|
|
|
margin-top: 3px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.credential-data + .credential-data {
|
|
|
|
margin-top: 1em;
|
|
|
|
}
|
|
|
|
|
|
|
|
.has-issues {
|
|
|
|
.credential-issues {
|
|
|
|
display: inline-block;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.headline {
|
|
|
|
font-weight: bold;
|
|
|
|
margin-bottom: 0.7em;
|
|
|
|
}
|
|
|
|
|
2021-08-29 04:36:17 -07:00
|
|
|
.credential-parameter-wrapper {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
.parameter-name {
|
|
|
|
font-weight: 400;
|
|
|
|
}
|
|
|
|
|
2021-08-29 04:36:17 -07:00
|
|
|
.parameter-value {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.credential-action {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|