mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-16 09:34:07 -08:00
317 lines
8.8 KiB
Vue
317 lines
8.8 KiB
Vue
|
<template>
|
||
|
<div v-if="credentialTypesNodeDescriptionDisplayed.length" class="node-credentials">
|
||
|
<credentials-edit :dialogVisible="credentialNewDialogVisible" :editCredentials="editCredentials" :setCredentialType="addType" :nodesInit="nodesInit" @closeDialog="closeCredentialNewDialog" @credentialsCreated="credentialsCreated" @credentialsUpdated="credentialsUpdated"></credentials-edit>
|
||
|
|
||
|
<div class="headline">
|
||
|
Credentials
|
||
|
</div>
|
||
|
|
||
|
<div v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed" :key="credentialTypeDescription.name" class="credential-data">
|
||
|
<el-row v-if="displayCredentials(credentialTypeDescription)">
|
||
|
|
||
|
<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':''">
|
||
|
<div class="credential-issues">
|
||
|
<el-tooltip placement="top" effect="light">
|
||
|
<div slot="content" v-html="'Issues:<br /> - ' + getIssues(credentialTypeDescription.name).join('<br /> - ')"></div>
|
||
|
<font-awesome-icon icon="exclamation-triangle" />
|
||
|
</el-tooltip>
|
||
|
</div>
|
||
|
<div :style="credentialInputWrapperStyle(credentialTypeDescription.name)">
|
||
|
<el-select v-model="credentials[credentialTypeDescription.name]" :disabled="isReadOnly" @change="credentialSelected(credentialTypeDescription.name)" placeholder="Select Credential" size="small">
|
||
|
<el-option
|
||
|
v-for="(item, index) in credentialOptions[credentialTypeDescription.name]"
|
||
|
:key="item.name + '_' + index"
|
||
|
:label="item.name"
|
||
|
:value="item.name">
|
||
|
</el-option>
|
||
|
</el-select>
|
||
|
</div>
|
||
|
</el-col>
|
||
|
<el-col :span="2" class="parameter-value">
|
||
|
<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 {
|
||
|
ICredentialsResponse,
|
||
|
INodeUi,
|
||
|
INodeUpdatePropertiesInformation,
|
||
|
IUpdateInformation,
|
||
|
} 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;
|
||
|
},
|
||
|
async credentialsCreated (data: ICredentialsResponse) {
|
||
|
await this.credentialsUpdated(data);
|
||
|
},
|
||
|
credentialsUpdated (data: ICredentialsResponse) {
|
||
|
if (!this.credentialTypesNode.includes(data.type)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.init();
|
||
|
Vue.set(this.credentials, data.type, data.name);
|
||
|
|
||
|
// Makes sure that it does also get set correctly on the node not just the UI
|
||
|
this.credentialSelected(data.type);
|
||
|
|
||
|
this.closeCredentialNewDialog();
|
||
|
},
|
||
|
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 credentials = this.credentials[credentialType];
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
.el-input input:hover {
|
||
|
border-width: 1px;
|
||
|
border-color: #ff8080;
|
||
|
border-style: solid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.headline {
|
||
|
font-weight: bold;
|
||
|
margin-bottom: 0.7em;
|
||
|
}
|
||
|
|
||
|
.parameter-name {
|
||
|
line-height: 2em;
|
||
|
font-weight: 400;
|
||
|
}
|
||
|
|
||
|
.update-credentials {
|
||
|
position: absolute;
|
||
|
top: 7px;
|
||
|
right: 3px;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
</style>
|