mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
refactor(editor): Convert credential related components to composition API (no-changelog) (#10530)
This commit is contained in:
parent
405c55a1f7
commit
402a8b40c0
|
@ -1130,10 +1130,7 @@ function resetCredentialData(): void {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
|
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
|
||||||
<CredentialInfo
|
<CredentialInfo :current-credential="currentCredential" />
|
||||||
:current-credential="currentCredential"
|
|
||||||
:credential-permissions="credentialPermissions"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
|
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
|
||||||
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
|
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
|
||||||
|
|
|
@ -1,57 +1,52 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import TimeAgo from '../TimeAgo.vue';
|
import TimeAgo from '../TimeAgo.vue';
|
||||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import type { ICredentialsDecryptedResponse, ICredentialsResponse } from '@/Interface';
|
||||||
|
import { N8nText } from 'n8n-design-system';
|
||||||
|
|
||||||
export default defineComponent({
|
type Props = {
|
||||||
name: 'CredentialInfo',
|
currentCredential: ICredentialsResponse | ICredentialsDecryptedResponse | null;
|
||||||
components: {
|
};
|
||||||
TimeAgo,
|
|
||||||
},
|
defineProps<Props>();
|
||||||
props: ['currentCredential', 'credentialPermissions'],
|
|
||||||
methods: {
|
const i18n = useI18n();
|
||||||
shortNodeType(nodeType: INodeTypeDescription) {
|
|
||||||
return this.$locale.shortNodeType(nodeType.name);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">
|
<N8nText :compact="true" :bold="true">
|
||||||
{{ $locale.baseText('credentialEdit.credentialInfo.created') }}
|
{{ i18n.baseText('credentialEdit.credentialInfo.created') }}
|
||||||
</n8n-text>
|
</N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"
|
<N8nText :compact="true"
|
||||||
><TimeAgo :date="currentCredential.createdAt" :capitalize="true"
|
><TimeAgo :date="currentCredential.createdAt" :capitalize="true"
|
||||||
/></n8n-text>
|
/></N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">
|
<N8nText :compact="true" :bold="true">
|
||||||
{{ $locale.baseText('credentialEdit.credentialInfo.lastModified') }}
|
{{ i18n.baseText('credentialEdit.credentialInfo.lastModified') }}
|
||||||
</n8n-text>
|
</N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true"
|
<N8nText :compact="true"
|
||||||
><TimeAgo :date="currentCredential.updatedAt" :capitalize="true"
|
><TimeAgo :date="currentCredential.updatedAt" :capitalize="true"
|
||||||
/></n8n-text>
|
/></N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
<el-col :span="8" :class="$style.label">
|
<el-col :span="8" :class="$style.label">
|
||||||
<n8n-text :compact="true" :bold="true">
|
<N8nText :compact="true" :bold="true">
|
||||||
{{ $locale.baseText('credentialEdit.credentialInfo.id') }}
|
{{ i18n.baseText('credentialEdit.credentialInfo.id') }}
|
||||||
</n8n-text>
|
</N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16" :class="$style.valueLabel">
|
<el-col :span="16" :class="$style.valueLabel">
|
||||||
<n8n-text :compact="true">{{ currentCredential.id }}</n8n-text>
|
<N8nText :compact="true">{{ currentCredential.id }}</N8nText>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,162 +1,114 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
import ProjectSharing from '@/components/Projects/ProjectSharing.vue';
|
||||||
ICredentialsResponse,
|
import { useI18n } from '@/composables/useI18n';
|
||||||
ICredentialsDecryptedResponse,
|
import { EnterpriseEditionFeature } from '@/constants';
|
||||||
IUserListAction,
|
import type { ICredentialsDecryptedResponse, ICredentialsResponse } from '@/Interface';
|
||||||
} from '@/Interface';
|
import type { PermissionsRecord } from '@/permissions';
|
||||||
import { defineComponent } from 'vue';
|
import { useProjectsStore } from '@/stores/projects.store';
|
||||||
import type { PropType } from 'vue';
|
import { useRolesStore } from '@/stores/roles.store';
|
||||||
import { useMessage } from '@/composables/useMessage';
|
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useUsersStore } from '@/stores/users.store';
|
||||||
import { useUsageStore } from '@/stores/usage.store';
|
|
||||||
import { EnterpriseEditionFeature } from '@/constants';
|
|
||||||
import ProjectSharing from '@/components/Projects/ProjectSharing.vue';
|
|
||||||
import { useProjectsStore } from '@/stores/projects.store';
|
|
||||||
import type { ProjectListItem, ProjectSharingData } from '@/types/projects.types';
|
import type { ProjectListItem, ProjectSharingData } from '@/types/projects.types';
|
||||||
import { ProjectTypes } from '@/types/projects.types';
|
import { ProjectTypes } from '@/types/projects.types';
|
||||||
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
|
||||||
import type { PermissionsRecord } from '@/permissions';
|
|
||||||
import type { EventBus } from 'n8n-design-system/utils';
|
|
||||||
import { useRolesStore } from '@/stores/roles.store';
|
|
||||||
import type { RoleMap } from '@/types/roles.types';
|
import type { RoleMap } from '@/types/roles.types';
|
||||||
import { splitName } from '@/utils/projects.utils';
|
import { splitName } from '@/utils/projects.utils';
|
||||||
|
import type { EventBus } from 'n8n-design-system/utils';
|
||||||
|
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
type Props = {
|
||||||
name: 'CredentialSharing',
|
credentialId: string;
|
||||||
components: {
|
credentialData: ICredentialDataDecryptedObject;
|
||||||
ProjectSharing,
|
credentialPermissions: PermissionsRecord['credential'];
|
||||||
},
|
credential?: ICredentialsResponse | ICredentialsDecryptedResponse | null;
|
||||||
props: {
|
modalBus: EventBus;
|
||||||
credential: {
|
};
|
||||||
type: Object as PropType<ICredentialsResponse | ICredentialsDecryptedResponse | null>,
|
|
||||||
default: null,
|
const props = withDefaults(defineProps<Props>(), { credential: null });
|
||||||
},
|
|
||||||
credentialId: {
|
const emit = defineEmits<{
|
||||||
type: String,
|
'update:modelValue': [value: ProjectSharingData[]];
|
||||||
required: true,
|
}>();
|
||||||
},
|
|
||||||
credentialData: {
|
const i18n = useI18n();
|
||||||
type: Object as PropType<ICredentialDataDecryptedObject>,
|
|
||||||
required: true,
|
const usersStore = useUsersStore();
|
||||||
},
|
const uiStore = useUIStore();
|
||||||
credentialPermissions: {
|
const settingsStore = useSettingsStore();
|
||||||
type: Object as PropType<PermissionsRecord['credential']>,
|
const projectsStore = useProjectsStore();
|
||||||
required: true,
|
const rolesStore = useRolesStore();
|
||||||
},
|
|
||||||
modalBus: {
|
const sharedWithProjects = ref([...(props.credential?.sharedWithProjects ?? [])]);
|
||||||
type: Object as PropType<EventBus>,
|
|
||||||
required: true,
|
const isSharingEnabled = computed(
|
||||||
},
|
() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing],
|
||||||
},
|
);
|
||||||
emits: ['update:modelValue'],
|
const credentialOwnerName = computed(() => {
|
||||||
setup() {
|
const { firstName, lastName, email } = splitName(props.credential?.homeProject?.name ?? '');
|
||||||
return {
|
|
||||||
...useMessage(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
sharedWithProjects: [...(this.credential?.sharedWithProjects ?? [])] as ProjectSharingData[],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapStores(
|
|
||||||
useCredentialsStore,
|
|
||||||
useUsersStore,
|
|
||||||
useUsageStore,
|
|
||||||
useUIStore,
|
|
||||||
useSettingsStore,
|
|
||||||
useProjectsStore,
|
|
||||||
useRolesStore,
|
|
||||||
),
|
|
||||||
usersListActions(): IUserListAction[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: this.$locale.baseText('credentialEdit.credentialSharing.list.delete'),
|
|
||||||
value: 'delete',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
isSharingEnabled(): boolean {
|
|
||||||
return this.settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing];
|
|
||||||
},
|
|
||||||
credentialOwnerName(): string {
|
|
||||||
const { firstName, lastName, email } = splitName(this.credential?.homeProject?.name ?? '');
|
|
||||||
return firstName || lastName ? `${firstName}${lastName ? ' ' + lastName : ''}` : email ?? '';
|
return firstName || lastName ? `${firstName}${lastName ? ' ' + lastName : ''}` : email ?? '';
|
||||||
},
|
});
|
||||||
credentialDataHomeProject(): ProjectSharingData | undefined {
|
|
||||||
|
const credentialDataHomeProject = computed<ProjectSharingData | undefined>(() => {
|
||||||
const credentialContainsProjectSharingData = (
|
const credentialContainsProjectSharingData = (
|
||||||
data: ICredentialDataDecryptedObject,
|
data: ICredentialDataDecryptedObject,
|
||||||
): data is { homeProject: ProjectSharingData } => {
|
): data is { homeProject: ProjectSharingData } => {
|
||||||
return 'homeProject' in data;
|
return 'homeProject' in data;
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.credentialData && credentialContainsProjectSharingData(this.credentialData)
|
return props.credentialData && credentialContainsProjectSharingData(props.credentialData)
|
||||||
? this.credentialData.homeProject
|
? props.credentialData.homeProject
|
||||||
: undefined;
|
: undefined;
|
||||||
},
|
});
|
||||||
isCredentialSharedWithCurrentUser(): boolean {
|
|
||||||
if (!Array.isArray(this.credentialData.sharedWithProjects)) return false;
|
|
||||||
|
|
||||||
return this.credentialData.sharedWithProjects.some((sharee) => {
|
const projects = computed<ProjectListItem[]>(() => {
|
||||||
return typeof sharee === 'object' && 'id' in sharee
|
return projectsStore.projects.filter(
|
||||||
? sharee.id === this.usersStore.currentUser?.id
|
|
||||||
: false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
projects(): ProjectListItem[] {
|
|
||||||
return this.projectsStore.projects.filter(
|
|
||||||
(project) =>
|
(project) =>
|
||||||
project.id !== this.credential?.homeProject?.id &&
|
project.id !== props.credential?.homeProject?.id &&
|
||||||
project.id !== this.credentialDataHomeProject?.id,
|
project.id !== credentialDataHomeProject.value?.id,
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
homeProject(): ProjectSharingData | undefined {
|
|
||||||
return this.credential?.homeProject ?? this.credentialDataHomeProject;
|
const homeProject = computed<ProjectSharingData | undefined>(
|
||||||
},
|
() => props.credential?.homeProject ?? credentialDataHomeProject.value,
|
||||||
isHomeTeamProject(): boolean {
|
);
|
||||||
return this.homeProject?.type === ProjectTypes.Team;
|
const isHomeTeamProject = computed(() => homeProject.value?.type === ProjectTypes.Team);
|
||||||
},
|
const credentialRoleTranslations = computed<Record<string, string>>(() => {
|
||||||
credentialRoleTranslations(): Record<string, string> {
|
|
||||||
return {
|
return {
|
||||||
'credential:user': this.$locale.baseText('credentialEdit.credentialSharing.role.user'),
|
'credential:user': i18n.baseText('credentialEdit.credentialSharing.role.user'),
|
||||||
};
|
};
|
||||||
},
|
});
|
||||||
credentialRoles(): RoleMap['credential'] {
|
|
||||||
return this.rolesStore.processedCredentialRoles.map(({ role, scopes, licensed }) => ({
|
const credentialRoles = computed<RoleMap['credential']>(() => {
|
||||||
|
return rolesStore.processedCredentialRoles.map(({ role, scopes, licensed }) => ({
|
||||||
role,
|
role,
|
||||||
name: this.credentialRoleTranslations[role],
|
name: credentialRoleTranslations.value[role],
|
||||||
scopes,
|
scopes,
|
||||||
licensed,
|
licensed,
|
||||||
}));
|
}));
|
||||||
},
|
|
||||||
sharingSelectPlaceholder() {
|
|
||||||
return this.projectsStore.teamProjects.length
|
|
||||||
? this.$locale.baseText('projects.sharing.select.placeholder.project')
|
|
||||||
: this.$locale.baseText('projects.sharing.select.placeholder.user');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
sharedWithProjects: {
|
|
||||||
handler(changedSharedWithProjects: ProjectSharingData[]) {
|
|
||||||
this.$emit('update:modelValue', changedSharedWithProjects);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await Promise.all([this.usersStore.fetchUsers(), this.projectsStore.getAllProjects()]);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goToUpgrade() {
|
|
||||||
void this.uiStore.goToUpgrade('credential_sharing', 'upgrade-credentials-sharing');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sharingSelectPlaceholder = computed(() =>
|
||||||
|
projectsStore.teamProjects.length
|
||||||
|
? i18n.baseText('projects.sharing.select.placeholder.project')
|
||||||
|
: i18n.baseText('projects.sharing.select.placeholder.user'),
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
sharedWithProjects,
|
||||||
|
(changedSharedWithProjects) => {
|
||||||
|
emit('update:modelValue', changedSharedWithProjects);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await Promise.all([usersStore.fetchUsers(), projectsStore.getAllProjects()]);
|
||||||
|
});
|
||||||
|
|
||||||
|
function goToUpgrade() {
|
||||||
|
void uiStore.goToUpgrade('credential_sharing', 'upgrade-credentials-sharing');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -164,33 +116,29 @@ export default defineComponent({
|
||||||
<div v-if="!isSharingEnabled">
|
<div v-if="!isSharingEnabled">
|
||||||
<N8nActionBox
|
<N8nActionBox
|
||||||
:heading="
|
:heading="
|
||||||
$locale.baseText(
|
i18n.baseText(uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.title)
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.title,
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
:description="
|
:description="
|
||||||
$locale.baseText(
|
i18n.baseText(
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.description,
|
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.description,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
:button-text="
|
:button-text="
|
||||||
$locale.baseText(
|
i18n.baseText(uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.button)
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.button,
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
@click:button="goToUpgrade"
|
@click:button="goToUpgrade"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<N8nInfoTip v-if="credentialPermissions.share" :bold="false" class="mb-s">
|
<N8nInfoTip v-if="credentialPermissions.share" :bold="false" class="mb-s">
|
||||||
{{ $locale.baseText('credentialEdit.credentialSharing.info.owner') }}
|
{{ i18n.baseText('credentialEdit.credentialSharing.info.owner') }}
|
||||||
</N8nInfoTip>
|
</N8nInfoTip>
|
||||||
<N8nInfoTip v-else-if="isHomeTeamProject" :bold="false" class="mb-s">
|
<N8nInfoTip v-else-if="isHomeTeamProject" :bold="false" class="mb-s">
|
||||||
{{ $locale.baseText('credentialEdit.credentialSharing.info.sharee.team') }}
|
{{ i18n.baseText('credentialEdit.credentialSharing.info.sharee.team') }}
|
||||||
</N8nInfoTip>
|
</N8nInfoTip>
|
||||||
<N8nInfoTip v-else :bold="false" class="mb-s">
|
<N8nInfoTip v-else :bold="false" class="mb-s">
|
||||||
{{
|
{{
|
||||||
$locale.baseText('credentialEdit.credentialSharing.info.sharee.personal', {
|
i18n.baseText('credentialEdit.credentialSharing.info.sharee.personal', {
|
||||||
interpolate: { credentialOwnerName },
|
interpolate: { credentialOwnerName },
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,56 +1,60 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ICredentialType } from 'n8n-workflow';
|
import type { ICredentialType, INodeProperties, NodeParameterValue } from 'n8n-workflow';
|
||||||
import { defineComponent } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import ScopesNotice from '@/components/ScopesNotice.vue';
|
import ScopesNotice from '@/components/ScopesNotice.vue';
|
||||||
import NodeCredentials from '@/components/NodeCredentials.vue';
|
import NodeCredentials from '@/components/NodeCredentials.vue';
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
|
import { N8nOption, N8nSelect } from 'n8n-design-system';
|
||||||
|
import type { INodeUi, INodeUpdatePropertiesInformation } from '@/Interface';
|
||||||
|
|
||||||
export default defineComponent({
|
type Props = {
|
||||||
name: 'CredentialsSelect',
|
activeCredentialType: string;
|
||||||
components: {
|
parameter: INodeProperties;
|
||||||
ScopesNotice,
|
node?: INodeUi;
|
||||||
NodeCredentials,
|
inputSize?: 'small' | 'large' | 'mini' | 'medium' | 'xlarge';
|
||||||
},
|
displayValue: NodeParameterValue;
|
||||||
props: [
|
isReadOnly: boolean;
|
||||||
'activeCredentialType',
|
displayTitle: string;
|
||||||
'node',
|
};
|
||||||
'parameter',
|
|
||||||
'inputSize',
|
|
||||||
'displayValue',
|
|
||||||
'isReadOnly',
|
|
||||||
'displayTitle',
|
|
||||||
],
|
|
||||||
emits: ['update:modelValue', 'setFocus', 'onBlur', 'credentialSelected'],
|
|
||||||
computed: {
|
|
||||||
...mapStores(useCredentialsStore),
|
|
||||||
allCredentialTypes(): ICredentialType[] {
|
|
||||||
return this.credentialsStore.allCredentialTypes;
|
|
||||||
},
|
|
||||||
scopes(): string[] {
|
|
||||||
if (!this.activeCredentialType) return [];
|
|
||||||
|
|
||||||
return this.credentialsStore.getScopesByCredentialType(this.activeCredentialType);
|
const props = defineProps<Props>();
|
||||||
},
|
|
||||||
supportedCredentialTypes(): ICredentialType[] {
|
const emit = defineEmits<{
|
||||||
return this.allCredentialTypes.filter((c: ICredentialType) => this.isSupported(c.name));
|
'update:modelValue': [value: string];
|
||||||
},
|
setFocus: [];
|
||||||
},
|
onBlur: [];
|
||||||
methods: {
|
credentialSelected: [update: INodeUpdatePropertiesInformation];
|
||||||
focus() {
|
}>();
|
||||||
const selectRef = this.$refs.innerSelect as HTMLElement | undefined;
|
|
||||||
if (selectRef) {
|
const credentialsStore = useCredentialsStore();
|
||||||
selectRef.focus();
|
|
||||||
|
const innerSelectRef = ref<HTMLSelectElement>();
|
||||||
|
|
||||||
|
const allCredentialTypes = computed(() => credentialsStore.allCredentialTypes);
|
||||||
|
const scopes = computed(() => {
|
||||||
|
if (!props.activeCredentialType) return [];
|
||||||
|
|
||||||
|
return credentialsStore.getScopesByCredentialType(props.activeCredentialType);
|
||||||
|
});
|
||||||
|
|
||||||
|
const supportedCredentialTypes = computed(() => {
|
||||||
|
return allCredentialTypes.value.filter((c: ICredentialType) => isSupported(c.name));
|
||||||
|
});
|
||||||
|
|
||||||
|
function focus() {
|
||||||
|
if (innerSelectRef.value) {
|
||||||
|
innerSelectRef.value.focus();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* Check if a credential type belongs to one of the supported sets defined
|
* Check if a credential type belongs to one of the supported sets defined
|
||||||
* in the `credentialTypes` key in a `credentialsSelect` parameter
|
* in the `credentialTypes` key in a `credentialsSelect` parameter
|
||||||
*/
|
*/
|
||||||
isSupported(name: string): boolean {
|
function isSupported(name: string): boolean {
|
||||||
const supported = this.getSupportedSets(this.parameter.credentialTypes);
|
const supported = getSupportedSets(props.parameter.credentialTypes ?? []);
|
||||||
|
|
||||||
const checkedCredType = this.credentialsStore.getCredentialTypeByName(name);
|
const checkedCredType = credentialsStore.getCredentialTypeByName(name);
|
||||||
if (!checkedCredType) return false;
|
if (!checkedCredType) return false;
|
||||||
|
|
||||||
for (const property of supported.has) {
|
for (const property of supported.has) {
|
||||||
|
@ -73,14 +77,15 @@ export default defineComponent({
|
||||||
// recurse upward until base credential type
|
// recurse upward until base credential type
|
||||||
// e.g. microsoftDynamicsOAuth2Api -> microsoftOAuth2Api -> oAuth2Api
|
// e.g. microsoftDynamicsOAuth2Api -> microsoftOAuth2Api -> oAuth2Api
|
||||||
return checkedCredType.extends.reduce(
|
return checkedCredType.extends.reduce(
|
||||||
(acc: boolean, parentType: string) => acc || this.isSupported(parentType),
|
(acc: boolean, parentType: string) => acc || isSupported(parentType),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
getSupportedSets(credentialTypes: string[]) {
|
|
||||||
|
function getSupportedSets(credentialTypes: string[]) {
|
||||||
return credentialTypes.reduce<{ extends: string[]; has: string[] }>(
|
return credentialTypes.reduce<{ extends: string[]; has: string[] }>(
|
||||||
(acc, cur) => {
|
(acc, cur) => {
|
||||||
const _extends = cur.split('extends:');
|
const _extends = cur.split('extends:');
|
||||||
|
@ -101,16 +106,16 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
{ extends: [], has: [] },
|
{ extends: [], has: [] },
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
defineExpose({ focus });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div :class="$style['parameter-value-container']">
|
<div :class="$style['parameter-value-container']">
|
||||||
<n8n-select
|
<N8nSelect
|
||||||
ref="innerSelect"
|
ref="innerSelectRef"
|
||||||
:size="inputSize"
|
:size="inputSize"
|
||||||
filterable
|
filterable
|
||||||
:model-value="displayValue"
|
:model-value="displayValue"
|
||||||
|
@ -118,12 +123,12 @@ export default defineComponent({
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
data-test-id="credential-select"
|
data-test-id="credential-select"
|
||||||
@update:model-value="(value: string) => $emit('update:modelValue', value)"
|
@update:model-value="(value: string) => emit('update:modelValue', value)"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@focus="$emit('setFocus')"
|
@focus="emit('setFocus')"
|
||||||
@blur="$emit('onBlur')"
|
@blur="emit('onBlur')"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="credType in supportedCredentialTypes"
|
v-for="credType in supportedCredentialTypes"
|
||||||
:key="credType.name"
|
:key="credType.name"
|
||||||
:value="credType.name"
|
:value="credType.name"
|
||||||
|
@ -135,8 +140,8 @@ export default defineComponent({
|
||||||
{{ credType.displayName }}
|
{{ credType.displayName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</N8nOption>
|
||||||
</n8n-select>
|
</N8nSelect>
|
||||||
<slot name="issues-and-options" />
|
<slot name="issues-and-options" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -147,10 +152,11 @@ export default defineComponent({
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<NodeCredentials
|
<NodeCredentials
|
||||||
|
v-if="node"
|
||||||
:node="node"
|
:node="node"
|
||||||
:readonly="isReadOnly"
|
:readonly="isReadOnly"
|
||||||
:override-cred-type="node.parameters[parameter.name]"
|
:override-cred-type="node?.parameters[parameter.name]"
|
||||||
@credential-selected="(updateInformation) => $emit('credentialSelected', updateInformation)"
|
@credential-selected="(updateInformation) => emit('credentialSelected', updateInformation)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,69 +1,59 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
import Modal from './Modal.vue';
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
import { CREDENTIAL_SELECT_MODAL_KEY } from '../constants';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { N8nButton, N8nSelect } from 'n8n-design-system';
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { CREDENTIAL_SELECT_MODAL_KEY } from '../constants';
|
||||||
|
import Modal from './Modal.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const externalHooks = useExternalHooks();
|
||||||
name: 'CredentialsSelectModal',
|
const telemetry = useTelemetry();
|
||||||
components: {
|
|
||||||
Modal,
|
const modalBus = ref(createEventBus());
|
||||||
},
|
const selected = ref('');
|
||||||
setup() {
|
const loading = ref(true);
|
||||||
const externalHooks = useExternalHooks();
|
const selectRef = ref<HTMLSelectElement>();
|
||||||
return {
|
|
||||||
externalHooks,
|
const credentialsStore = useCredentialsStore();
|
||||||
};
|
const uiStore = useUIStore();
|
||||||
},
|
const workflowsStore = useWorkflowsStore();
|
||||||
data() {
|
|
||||||
return {
|
onMounted(async () => {
|
||||||
modalBus: createEventBus(),
|
|
||||||
selected: '',
|
|
||||||
loading: true,
|
|
||||||
CREDENTIAL_SELECT_MODAL_KEY,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
try {
|
try {
|
||||||
await this.credentialsStore.fetchCredentialTypes(false);
|
await credentialsStore.fetchCredentialTypes(false);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
this.loading = false;
|
|
||||||
|
loading.value = false;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const elementRef = this.$refs.select as HTMLSelectElement | undefined;
|
if (selectRef.value) {
|
||||||
if (elementRef) {
|
selectRef.value.focus();
|
||||||
elementRef.focus();
|
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
},
|
});
|
||||||
computed: {
|
|
||||||
...mapStores(useCredentialsStore, useUIStore, useWorkflowsStore),
|
function onSelect(type: string) {
|
||||||
},
|
selected.value = type;
|
||||||
methods: {
|
}
|
||||||
onSelect(type: string) {
|
|
||||||
this.selected = type;
|
function openCredentialType() {
|
||||||
},
|
modalBus.value.emit('close');
|
||||||
openCredentialType() {
|
uiStore.openNewCredential(selected.value);
|
||||||
this.modalBus.emit('close');
|
|
||||||
this.uiStore.openNewCredential(this.selected);
|
|
||||||
|
|
||||||
const telemetryPayload = {
|
const telemetryPayload = {
|
||||||
credential_type: this.selected,
|
credential_type: selected.value,
|
||||||
source: 'primary_menu',
|
source: 'primary_menu',
|
||||||
new_credential: true,
|
new_credential: true,
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: workflowsStore.workflowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$telemetry.track('User opened Credential modal', telemetryPayload);
|
telemetry.track('User opened Credential modal', telemetryPayload);
|
||||||
void this.externalHooks.run('credentialsSelectModal.openCredentialType', telemetryPayload);
|
void externalHooks.run('credentialsSelectModal.openCredentialType', telemetryPayload);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -86,8 +76,8 @@ export default defineComponent({
|
||||||
<div :class="$style.subtitle">
|
<div :class="$style.subtitle">
|
||||||
{{ $locale.baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}
|
{{ $locale.baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}
|
||||||
</div>
|
</div>
|
||||||
<n8n-select
|
<N8nSelect
|
||||||
ref="select"
|
ref="selectRef"
|
||||||
filterable
|
filterable
|
||||||
default-first-option
|
default-first-option
|
||||||
:placeholder="$locale.baseText('credentialSelectModal.searchForApp')"
|
:placeholder="$locale.baseText('credentialSelectModal.searchForApp')"
|
||||||
|
@ -99,7 +89,7 @@ export default defineComponent({
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<font-awesome-icon icon="search" />
|
<font-awesome-icon icon="search" />
|
||||||
</template>
|
</template>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="credential in credentialsStore.allCredentialTypes"
|
v-for="credential in credentialsStore.allCredentialTypes"
|
||||||
:key="credential.name"
|
:key="credential.name"
|
||||||
:value="credential.name"
|
:value="credential.name"
|
||||||
|
@ -107,12 +97,12 @@ export default defineComponent({
|
||||||
filterable
|
filterable
|
||||||
data-test-id="new-credential-type-select-option"
|
data-test-id="new-credential-type-select-option"
|
||||||
/>
|
/>
|
||||||
</n8n-select>
|
</N8nSelect>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<n8n-button
|
<N8nButton
|
||||||
:label="$locale.baseText('credentialSelectModal.continue')"
|
:label="$locale.baseText('credentialSelectModal.continue')"
|
||||||
float="right"
|
float="right"
|
||||||
size="large"
|
size="large"
|
||||||
|
|
|
@ -1,176 +1,158 @@
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import type { ICredentialsResponse, INodeUi, INodeUpdatePropertiesInformation } from '@/Interface';
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import type {
|
|
||||||
ICredentialsResponse,
|
|
||||||
INodeUi,
|
|
||||||
INodeUpdatePropertiesInformation,
|
|
||||||
IUser,
|
|
||||||
} from '@/Interface';
|
|
||||||
import type {
|
import type {
|
||||||
INodeCredentialDescription,
|
INodeCredentialDescription,
|
||||||
INodeCredentialsDetails,
|
INodeCredentialsDetails,
|
||||||
INodeParameters,
|
NodeParameterValueType,
|
||||||
INodeProperties,
|
|
||||||
INodeTypeDescription,
|
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
|
|
||||||
import TitledList from '@/components/TitledList.vue';
|
import TitledList from '@/components/TitledList.vue';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { CREDENTIAL_ONLY_NODE_PREFIX, KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { ndvEventBus } from '@/event-bus';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { CREDENTIAL_ONLY_NODE_PREFIX, KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
import { assert } from '@/utils/assert';
|
||||||
import {
|
import {
|
||||||
|
getAllNodeCredentialForAuthType,
|
||||||
getAuthTypeForNodeCredential,
|
getAuthTypeForNodeCredential,
|
||||||
getMainAuthField,
|
getMainAuthField,
|
||||||
getNodeCredentialForSelectedAuthType,
|
getNodeCredentialForSelectedAuthType,
|
||||||
getAllNodeCredentialForAuthType,
|
|
||||||
updateNodeAuthType,
|
|
||||||
isRequiredCredential,
|
isRequiredCredential,
|
||||||
|
updateNodeAuthType,
|
||||||
} from '@/utils/nodeTypesUtils';
|
} from '@/utils/nodeTypesUtils';
|
||||||
import { assert } from '@/utils/assert';
|
import {
|
||||||
import { ndvEventBus } from '@/event-bus';
|
N8nInput,
|
||||||
|
N8nInputLabel,
|
||||||
|
N8nOption,
|
||||||
|
N8nSelect,
|
||||||
|
N8nText,
|
||||||
|
N8nTooltip,
|
||||||
|
} from 'n8n-design-system';
|
||||||
|
|
||||||
interface CredentialDropdownOption extends ICredentialsResponse {
|
interface CredentialDropdownOption extends ICredentialsResponse {
|
||||||
typeDisplayName: string;
|
typeDisplayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
type Props = {
|
||||||
name: 'NodeCredentials',
|
node: INodeUi;
|
||||||
components: {
|
overrideCredType?: NodeParameterValueType;
|
||||||
TitledList,
|
readonly?: boolean;
|
||||||
},
|
showAll?: boolean;
|
||||||
props: {
|
hideIssues?: boolean;
|
||||||
readonly: {
|
};
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
type: Object as PropType<INodeUi>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
overrideCredType: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
showAll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
hideIssues: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: { credentialSelected: null, valueChanged: null, blur: null },
|
|
||||||
setup() {
|
|
||||||
const nodeHelpers = useNodeHelpers();
|
|
||||||
|
|
||||||
return {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
...useToast(),
|
readonly: false,
|
||||||
nodeHelpers,
|
overrideCredType: '',
|
||||||
};
|
showAll: false,
|
||||||
},
|
hideIssues: false,
|
||||||
data() {
|
});
|
||||||
return {
|
|
||||||
NEW_CREDENTIALS_TEXT: `- ${this.$locale.baseText('nodeCredentials.createNew')} -`,
|
const emit = defineEmits<{
|
||||||
subscribedToCredentialType: '',
|
credentialSelected: [credential: INodeUpdatePropertiesInformation];
|
||||||
listeningForAuthChange: false,
|
valueChanged: [value: { name: string; value: string }];
|
||||||
};
|
blur: [source: string];
|
||||||
},
|
}>();
|
||||||
computed: {
|
|
||||||
...mapStores(
|
const telemetry = useTelemetry();
|
||||||
useCredentialsStore,
|
const i18n = useI18n();
|
||||||
useNodeTypesStore,
|
const NEW_CREDENTIALS_TEXT = `- ${i18n.baseText('nodeCredentials.createNew')} -`;
|
||||||
useNDVStore,
|
|
||||||
useUIStore,
|
const credentialsStore = useCredentialsStore();
|
||||||
useUsersStore,
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
useWorkflowsStore,
|
const ndvStore = useNDVStore();
|
||||||
),
|
const uiStore = useUIStore();
|
||||||
currentUser(): IUser {
|
const workflowsStore = useWorkflowsStore();
|
||||||
return this.usersStore.currentUser ?? ({} as IUser);
|
|
||||||
},
|
const nodeHelpers = useNodeHelpers();
|
||||||
credentialTypesNode(): string[] {
|
|
||||||
return this.credentialTypesNodeDescription.map(
|
const toast = useToast();
|
||||||
|
|
||||||
|
const subscribedToCredentialType = ref('');
|
||||||
|
const listeningForAuthChange = ref(false);
|
||||||
|
|
||||||
|
const credentialTypesNode = computed(() =>
|
||||||
|
credentialTypesNodeDescription.value.map(
|
||||||
(credentialTypeDescription) => credentialTypeDescription.name,
|
(credentialTypeDescription) => credentialTypeDescription.name,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
credentialTypesNodeDescriptionDisplayed(): INodeCredentialDescription[] {
|
|
||||||
return this.credentialTypesNodeDescription.filter((credentialTypeDescription) => {
|
const credentialTypesNodeDescriptionDisplayed = computed(() =>
|
||||||
return this.displayCredentials(credentialTypeDescription);
|
credentialTypesNodeDescription.value.filter((credentialTypeDescription) =>
|
||||||
});
|
displayCredentials(credentialTypeDescription),
|
||||||
},
|
),
|
||||||
credentialTypesNodeDescription(): INodeCredentialDescription[] {
|
);
|
||||||
const credType = this.credentialsStore.getCredentialTypeByName(this.overrideCredType);
|
const credentialTypesNodeDescription = computed(() => {
|
||||||
|
if (typeof props.overrideCredType !== 'string') return [];
|
||||||
|
|
||||||
|
const credType = credentialsStore.getCredentialTypeByName(props.overrideCredType);
|
||||||
|
|
||||||
if (credType) return [credType];
|
if (credType) return [credType];
|
||||||
|
|
||||||
const activeNodeType = this.nodeType;
|
const activeNodeType = nodeType.value;
|
||||||
if (activeNodeType?.credentials) {
|
if (activeNodeType?.credentials) {
|
||||||
return activeNodeType.credentials;
|
return activeNodeType.credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
},
|
});
|
||||||
credentialTypeNames() {
|
|
||||||
|
const credentialTypeNames = computed(() => {
|
||||||
const returnData: Record<string, string> = {};
|
const returnData: Record<string, string> = {};
|
||||||
for (const credentialTypeName of this.credentialTypesNode) {
|
for (const credentialTypeName of credentialTypesNode.value) {
|
||||||
const credentialType = this.credentialsStore.getCredentialTypeByName(credentialTypeName);
|
const credentialType = credentialsStore.getCredentialTypeByName(credentialTypeName);
|
||||||
returnData[credentialTypeName] = credentialType
|
returnData[credentialTypeName] = credentialType
|
||||||
? credentialType.displayName
|
? credentialType.displayName
|
||||||
: credentialTypeName;
|
: credentialTypeName;
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
});
|
||||||
selected(): Record<string, INodeCredentialsDetails> {
|
|
||||||
return this.node.credentials ?? {};
|
const selected = computed<Record<string, INodeCredentialsDetails>>(
|
||||||
},
|
() => props.node.credentials ?? {},
|
||||||
nodeType(): INodeTypeDescription | null {
|
);
|
||||||
return this.nodeTypesStore.getNodeType(this.node.type, this.node.typeVersion);
|
const nodeType = computed(() =>
|
||||||
},
|
nodeTypesStore.getNodeType(props.node.type, props.node.typeVersion),
|
||||||
mainNodeAuthField(): INodeProperties | null {
|
);
|
||||||
return getMainAuthField(this.nodeType);
|
const mainNodeAuthField = computed(() => getMainAuthField(nodeType.value));
|
||||||
},
|
watch(
|
||||||
},
|
() => props.node.parameters,
|
||||||
watch: {
|
(newValue, oldValue) => {
|
||||||
'node.parameters': {
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
handler(newValue: INodeParameters, oldValue: INodeParameters) {
|
|
||||||
// When active node parameters change, check if authentication type has been changed
|
// When active node parameters change, check if authentication type has been changed
|
||||||
// and set `subscribedToCredentialType` to corresponding credential type
|
// and set `subscribedToCredentialType` to corresponding credential type
|
||||||
const isActive = this.node.name === this.ndvStore.activeNode?.name;
|
const isActive = props.node.name === ndvStore.activeNode?.name;
|
||||||
const nodeType = this.nodeType;
|
|
||||||
// Only do this for active node and if it's listening for auth change
|
// Only do this for active node and if it's listening for auth change
|
||||||
if (isActive && nodeType && this.listeningForAuthChange) {
|
if (isActive && nodeType.value && listeningForAuthChange.value) {
|
||||||
if (this.mainNodeAuthField && oldValue && newValue) {
|
if (mainNodeAuthField.value && oldValue && newValue) {
|
||||||
const newAuth = newValue[this.mainNodeAuthField.name];
|
const newAuth = newValue[mainNodeAuthField.value.name];
|
||||||
|
|
||||||
if (newAuth) {
|
if (newAuth) {
|
||||||
const authType =
|
const authType =
|
||||||
typeof newAuth === 'object' ? JSON.stringify(newAuth) : newAuth.toString();
|
typeof newAuth === 'object' ? JSON.stringify(newAuth) : newAuth.toString();
|
||||||
const credentialType = getNodeCredentialForSelectedAuthType(nodeType, authType);
|
const credentialType = getNodeCredentialForSelectedAuthType(nodeType.value, authType);
|
||||||
if (credentialType) {
|
if (credentialType) {
|
||||||
this.subscribedToCredentialType = credentialType.name;
|
subscribedToCredentialType.value = credentialType.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
{ immediate: true, deep: true },
|
||||||
},
|
);
|
||||||
mounted() {
|
|
||||||
// Listen for credentials store changes so credential selection can be updated if creds are changed from the modal
|
onMounted(() => {
|
||||||
this.credentialsStore.$onAction(({ name, after, args }) => {
|
credentialsStore.$onAction(({ name, after, args }) => {
|
||||||
const listeningForActions = ['createNewCredential', 'updateCredential', 'deleteCredential'];
|
const listeningForActions = ['createNewCredential', 'updateCredential', 'deleteCredential'];
|
||||||
const credentialType = this.subscribedToCredentialType;
|
const credentialType = subscribedToCredentialType.value;
|
||||||
if (!credentialType) {
|
if (!credentialType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -179,42 +161,40 @@ export default defineComponent({
|
||||||
if (!listeningForActions.includes(name)) {
|
if (!listeningForActions.includes(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const current = this.selected[credentialType];
|
const current = selected.value[credentialType];
|
||||||
let credentialsOfType: ICredentialsResponse[] = [];
|
let credentialsOfType: ICredentialsResponse[] = [];
|
||||||
if (this.showAll) {
|
if (props.showAll) {
|
||||||
if (this.node) {
|
if (props.node) {
|
||||||
credentialsOfType = [
|
credentialsOfType = [...(credentialsStore.allUsableCredentialsForNode(props.node) || [])];
|
||||||
...(this.credentialsStore.allUsableCredentialsForNode(this.node) || []),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
credentialsOfType = [
|
credentialsOfType = [
|
||||||
...(this.credentialsStore.allUsableCredentialsByType[credentialType] || []),
|
...(credentialsStore.allUsableCredentialsByType[credentialType] || []),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
switch (name) {
|
switch (name) {
|
||||||
// new credential was added
|
// new credential was added
|
||||||
case 'createNewCredential':
|
case 'createNewCredential':
|
||||||
if (result) {
|
if (result) {
|
||||||
this.onCredentialSelected(credentialType, (result as ICredentialsResponse).id);
|
onCredentialSelected(credentialType, (result as ICredentialsResponse).id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'updateCredential':
|
case 'updateCredential':
|
||||||
const updatedCredential = result as ICredentialsResponse;
|
const updatedCredential = result as ICredentialsResponse;
|
||||||
// credential name was changed, update it
|
// credential name was changed, update it
|
||||||
if (updatedCredential.name !== current.name) {
|
if (updatedCredential.name !== current.name) {
|
||||||
this.onCredentialSelected(credentialType, current.id);
|
onCredentialSelected(credentialType, current.id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'deleteCredential':
|
case 'deleteCredential':
|
||||||
// all credentials were deleted
|
// all credentials were deleted
|
||||||
if (credentialsOfType.length === 0) {
|
if (credentialsOfType.length === 0) {
|
||||||
this.clearSelectedCredential(credentialType);
|
clearSelectedCredential(credentialType);
|
||||||
} else {
|
} else {
|
||||||
const id = args[0].id;
|
const id = args[0].id;
|
||||||
// credential was deleted, select last one added to replace with
|
// credential was deleted, select last one added to replace with
|
||||||
if (current.id === id) {
|
if (current.id === id) {
|
||||||
this.onCredentialSelected(
|
onCredentialSelected(
|
||||||
credentialType,
|
credentialType,
|
||||||
credentialsOfType[credentialsOfType.length - 1].id,
|
credentialsOfType[credentialsOfType.length - 1].id,
|
||||||
);
|
);
|
||||||
|
@ -225,170 +205,160 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ndvEventBus.on('credential.createNew', this.onCreateAndAssignNewCredential);
|
ndvEventBus.on('credential.createNew', onCreateAndAssignNewCredential);
|
||||||
},
|
});
|
||||||
beforeUnmount() {
|
|
||||||
ndvEventBus.off('credential.createNew', this.onCreateAndAssignNewCredential);
|
onBeforeUnmount(() => {
|
||||||
},
|
ndvEventBus.off('credential.createNew', onCreateAndAssignNewCredential);
|
||||||
methods: {
|
});
|
||||||
getAllRelatedCredentialTypes(credentialType: INodeCredentialDescription): string[] {
|
|
||||||
const credentialIsRequired = this.showMixedCredentials(credentialType);
|
function getAllRelatedCredentialTypes(credentialType: INodeCredentialDescription): string[] {
|
||||||
|
const credentialIsRequired = showMixedCredentials(credentialType);
|
||||||
if (credentialIsRequired) {
|
if (credentialIsRequired) {
|
||||||
if (this.mainNodeAuthField) {
|
if (mainNodeAuthField.value) {
|
||||||
const credentials = getAllNodeCredentialForAuthType(
|
const credentials = getAllNodeCredentialForAuthType(
|
||||||
this.nodeType,
|
nodeType.value,
|
||||||
this.mainNodeAuthField.name,
|
mainNodeAuthField.value.name,
|
||||||
);
|
);
|
||||||
return credentials.map((cred) => cred.name);
|
return credentials.map((cred) => cred.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [credentialType.name];
|
return [credentialType.name];
|
||||||
},
|
}
|
||||||
getCredentialOptions(types: string[]): CredentialDropdownOption[] {
|
|
||||||
|
function getCredentialOptions(types: string[]): CredentialDropdownOption[] {
|
||||||
let options: CredentialDropdownOption[] = [];
|
let options: CredentialDropdownOption[] = [];
|
||||||
types.forEach((type) => {
|
types.forEach((type) => {
|
||||||
options = options.concat(
|
options = options.concat(
|
||||||
this.credentialsStore.allUsableCredentialsByType[type].map(
|
credentialsStore.allUsableCredentialsByType[type].map(
|
||||||
(option: ICredentialsResponse) =>
|
(option: ICredentialsResponse) =>
|
||||||
({
|
({
|
||||||
...option,
|
...option,
|
||||||
typeDisplayName: this.credentialsStore.getCredentialTypeByName(type)?.displayName,
|
typeDisplayName: credentialsStore.getCredentialTypeByName(type)?.displayName,
|
||||||
}) as CredentialDropdownOption,
|
}) as CredentialDropdownOption,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return options;
|
return options;
|
||||||
},
|
}
|
||||||
getSelectedId(type: string) {
|
|
||||||
if (this.isCredentialExisting(type)) {
|
function getSelectedId(type: string) {
|
||||||
return this.selected[type].id;
|
if (isCredentialExisting(type)) {
|
||||||
|
return selected.value[type].id;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
}
|
||||||
getSelectedName(type: string) {
|
|
||||||
return this.selected?.[type]?.name;
|
function getSelectedName(type: string) {
|
||||||
},
|
return selected.value?.[type]?.name;
|
||||||
getSelectPlaceholder(type: string, issues: string[]) {
|
}
|
||||||
return issues.length && this.getSelectedName(type)
|
|
||||||
? this.$locale.baseText('nodeCredentials.selectedCredentialUnavailable', {
|
function getSelectPlaceholder(type: string, issues: string[]) {
|
||||||
interpolate: { name: this.getSelectedName(type) },
|
return issues.length && getSelectedName(type)
|
||||||
|
? i18n.baseText('nodeCredentials.selectedCredentialUnavailable', {
|
||||||
|
interpolate: { name: getSelectedName(type) },
|
||||||
})
|
})
|
||||||
: this.$locale.baseText('nodeCredentials.selectCredential');
|
: i18n.baseText('nodeCredentials.selectCredential');
|
||||||
},
|
}
|
||||||
credentialInputWrapperStyle(credentialType: string) {
|
|
||||||
let deductWidth = 0;
|
|
||||||
const styles = {
|
|
||||||
width: '100%',
|
|
||||||
};
|
|
||||||
if (this.getIssues(credentialType).length) {
|
|
||||||
deductWidth += 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deductWidth !== 0) {
|
function clearSelectedCredential(credentialType: string) {
|
||||||
styles.width = `calc(100% - ${deductWidth}px)`;
|
const node = props.node;
|
||||||
}
|
|
||||||
|
|
||||||
return styles;
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSelectedCredential(credentialType: string) {
|
|
||||||
const node: INodeUi = this.node;
|
|
||||||
|
|
||||||
const credentials = {
|
const credentials = {
|
||||||
...(node.credentials || {}),
|
...(node.credentials ?? {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
delete credentials[credentialType];
|
delete credentials[credentialType];
|
||||||
|
|
||||||
const updateInformation: INodeUpdatePropertiesInformation = {
|
const updateInformation: INodeUpdatePropertiesInformation = {
|
||||||
name: this.node.name,
|
name: props.node.name,
|
||||||
properties: {
|
properties: {
|
||||||
credentials,
|
credentials,
|
||||||
position: this.node.position,
|
position: props.node.position,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$emit('credentialSelected', updateInformation);
|
emit('credentialSelected', updateInformation);
|
||||||
},
|
}
|
||||||
|
|
||||||
createNewCredential(
|
function createNewCredential(
|
||||||
credentialType: string,
|
credentialType: string,
|
||||||
listenForAuthChange: boolean = false,
|
listenForAuthChange: boolean = false,
|
||||||
showAuthOptions = false,
|
showAuthOptions = false,
|
||||||
) {
|
) {
|
||||||
if (listenForAuthChange) {
|
if (listenForAuthChange) {
|
||||||
// If new credential dialog is open, start listening for auth type change which should happen in the modal
|
// If new credential dialog is open, start listening for auth type change which should happen in the modal
|
||||||
// this will be handled in this component's watcher which will set subscribed credential accordingly
|
// this will be handled in this component's watcher which will set subscribed credential accordingly
|
||||||
this.listeningForAuthChange = true;
|
listeningForAuthChange.value = true;
|
||||||
this.subscribedToCredentialType = credentialType;
|
subscribedToCredentialType.value = credentialType;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.uiStore.openNewCredential(credentialType, showAuthOptions);
|
uiStore.openNewCredential(credentialType, showAuthOptions);
|
||||||
this.$telemetry.track('User opened Credential modal', {
|
telemetry.track('User opened Credential modal', {
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
source: 'node',
|
source: 'node',
|
||||||
new_credential: true,
|
new_credential: true,
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: workflowsStore.workflowId,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onCreateAndAssignNewCredential({
|
function onCreateAndAssignNewCredential({
|
||||||
type,
|
type,
|
||||||
showAuthOptions,
|
showAuthOptions,
|
||||||
}: {
|
}: {
|
||||||
type: string;
|
type: string;
|
||||||
showAuthOptions: boolean;
|
showAuthOptions: boolean;
|
||||||
}) {
|
}) {
|
||||||
this.createNewCredential(type, true, showAuthOptions);
|
createNewCredential(type, true, showAuthOptions);
|
||||||
},
|
}
|
||||||
|
|
||||||
onCredentialSelected(
|
function onCredentialSelected(
|
||||||
credentialType: string,
|
credentialType: string,
|
||||||
credentialId: string | null | undefined,
|
credentialId: string | null | undefined,
|
||||||
showAuthOptions = false,
|
showAuthOptions = false,
|
||||||
) {
|
) {
|
||||||
const newCredentialOptionSelected = credentialId === this.NEW_CREDENTIALS_TEXT;
|
const newCredentialOptionSelected = credentialId === NEW_CREDENTIALS_TEXT;
|
||||||
if (!credentialId || newCredentialOptionSelected) {
|
if (!credentialId || newCredentialOptionSelected) {
|
||||||
this.createNewCredential(credentialType, newCredentialOptionSelected, showAuthOptions);
|
createNewCredential(credentialType, newCredentialOptionSelected, showAuthOptions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$telemetry.track('User selected credential from node modal', {
|
telemetry.track('User selected credential from node modal', {
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
node_type: this.node.type,
|
node_type: props.node.type,
|
||||||
...(this.nodeHelpers.hasProxyAuth(this.node) ? { is_service_specific: true } : {}),
|
...(nodeHelpers.hasProxyAuth(props.node) ? { is_service_specific: true } : {}),
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: workflowsStore.workflowId,
|
||||||
credential_id: credentialId,
|
credential_id: credentialId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedCredentials = this.credentialsStore.getCredentialById(credentialId);
|
const selectedCredentials = credentialsStore.getCredentialById(credentialId);
|
||||||
const selectedCredentialsType = this.showAll ? selectedCredentials.type : credentialType;
|
const selectedCredentialsType = props.showAll ? selectedCredentials.type : credentialType;
|
||||||
const oldCredentials = this.node.credentials?.[selectedCredentialsType] ?? null;
|
const oldCredentials = props.node.credentials?.[selectedCredentialsType] ?? null;
|
||||||
|
|
||||||
const selected = { id: selectedCredentials.id, name: selectedCredentials.name };
|
const newSelectedCredentials: INodeCredentialsDetails = {
|
||||||
|
id: selectedCredentials.id,
|
||||||
|
name: selectedCredentials.name,
|
||||||
|
};
|
||||||
|
|
||||||
// if credentials has been string or neither id matched nor name matched uniquely
|
// if credentials has been string or neither id matched nor name matched uniquely
|
||||||
if (
|
if (
|
||||||
oldCredentials?.id === null ||
|
oldCredentials?.id === null ||
|
||||||
(oldCredentials?.id &&
|
(oldCredentials?.id &&
|
||||||
!this.credentialsStore.getCredentialByIdAndType(
|
!credentialsStore.getCredentialByIdAndType(oldCredentials.id, selectedCredentialsType))
|
||||||
oldCredentials.id,
|
|
||||||
selectedCredentialsType,
|
|
||||||
))
|
|
||||||
) {
|
) {
|
||||||
// update all nodes in the workflow with the same old/invalid credentials
|
// update all nodes in the workflow with the same old/invalid credentials
|
||||||
this.workflowsStore.replaceInvalidWorkflowCredentials({
|
workflowsStore.replaceInvalidWorkflowCredentials({
|
||||||
credentials: selected,
|
credentials: newSelectedCredentials,
|
||||||
invalid: oldCredentials,
|
invalid: oldCredentials,
|
||||||
type: selectedCredentialsType,
|
type: selectedCredentialsType,
|
||||||
});
|
});
|
||||||
this.nodeHelpers.updateNodesCredentialsIssues();
|
nodeHelpers.updateNodesCredentialsIssues();
|
||||||
this.showMessage({
|
toast.showMessage({
|
||||||
title: this.$locale.baseText('nodeCredentials.showMessage.title'),
|
title: i18n.baseText('nodeCredentials.showMessage.title'),
|
||||||
message: this.$locale.baseText('nodeCredentials.showMessage.message', {
|
message: i18n.baseText('nodeCredentials.showMessage.message', {
|
||||||
interpolate: {
|
interpolate: {
|
||||||
oldCredentialName: oldCredentials.name,
|
oldCredentialName: oldCredentials.name,
|
||||||
newCredentialName: selected.name,
|
newCredentialName: newSelectedCredentials.name,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -396,54 +366,54 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
// If credential is selected from mixed credential dropdown, update node's auth filed based on selected credential
|
// If credential is selected from mixed credential dropdown, update node's auth filed based on selected credential
|
||||||
if (this.showAll && this.mainNodeAuthField) {
|
if (props.showAll && mainNodeAuthField.value) {
|
||||||
const nodeCredentialDescription = this.nodeType?.credentials?.find(
|
const nodeCredentialDescription = nodeType.value?.credentials?.find(
|
||||||
(cred) => cred.name === selectedCredentialsType,
|
(cred) => cred.name === selectedCredentialsType,
|
||||||
);
|
);
|
||||||
const authOption = getAuthTypeForNodeCredential(this.nodeType, nodeCredentialDescription);
|
const authOption = getAuthTypeForNodeCredential(nodeType.value, nodeCredentialDescription);
|
||||||
if (authOption) {
|
if (authOption) {
|
||||||
updateNodeAuthType(this.node, authOption.value);
|
updateNodeAuthType(props.node, authOption.value);
|
||||||
const parameterData = {
|
const parameterData = {
|
||||||
name: `parameters.${this.mainNodeAuthField.name}`,
|
name: `parameters.${mainNodeAuthField.value.name}`,
|
||||||
value: authOption.value,
|
value: authOption.value,
|
||||||
};
|
};
|
||||||
this.$emit('valueChanged', parameterData);
|
emit('valueChanged', parameterData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const node: INodeUi = this.node;
|
const node = props.node;
|
||||||
|
|
||||||
const credentials = {
|
const credentials = {
|
||||||
...(node.credentials ?? {}),
|
...(node.credentials ?? {}),
|
||||||
[selectedCredentialsType]: selected,
|
[selectedCredentialsType]: newSelectedCredentials,
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateInformation: INodeUpdatePropertiesInformation = {
|
const updateInformation: INodeUpdatePropertiesInformation = {
|
||||||
name: this.node.name,
|
name: props.node.name,
|
||||||
properties: {
|
properties: {
|
||||||
credentials,
|
credentials,
|
||||||
position: this.node.position,
|
position: props.node.position,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$emit('credentialSelected', updateInformation);
|
emit('credentialSelected', updateInformation);
|
||||||
},
|
}
|
||||||
|
|
||||||
displayCredentials(credentialTypeDescription: INodeCredentialDescription): boolean {
|
function displayCredentials(credentialTypeDescription: INodeCredentialDescription): boolean {
|
||||||
if (credentialTypeDescription.displayOptions === undefined) {
|
if (credentialTypeDescription.displayOptions === undefined) {
|
||||||
// If it is not defined no need to do a proper check
|
// If it is not defined no need to do a proper check
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this.nodeHelpers.displayParameter(
|
return nodeHelpers.displayParameter(
|
||||||
this.node.parameters,
|
props.node.parameters,
|
||||||
credentialTypeDescription,
|
credentialTypeDescription,
|
||||||
'',
|
'',
|
||||||
this.node,
|
props.node,
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
getIssues(credentialTypeName: string): string[] {
|
function getIssues(credentialTypeName: string): string[] {
|
||||||
const node = this.node;
|
const node = props.node;
|
||||||
|
|
||||||
if (node.issues?.credentials === undefined) {
|
if (node.issues?.credentials === undefined) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -453,58 +423,57 @@ export default defineComponent({
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return node.issues.credentials[credentialTypeName];
|
return node.issues.credentials[credentialTypeName];
|
||||||
},
|
}
|
||||||
|
|
||||||
isCredentialExisting(credentialType: string): boolean {
|
function isCredentialExisting(credentialType: string): boolean {
|
||||||
if (!this.node.credentials?.[credentialType]?.id) {
|
if (!props.node.credentials?.[credentialType]?.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { id } = this.node.credentials[credentialType];
|
const { id } = props.node.credentials[credentialType];
|
||||||
const options = this.getCredentialOptions([credentialType]);
|
const options = getCredentialOptions([credentialType]);
|
||||||
|
|
||||||
return !!options.find((option: ICredentialsResponse) => option.id === id);
|
return !!options.find((option: ICredentialsResponse) => option.id === id);
|
||||||
},
|
}
|
||||||
|
|
||||||
editCredential(credentialType: string): void {
|
function editCredential(credentialType: string): void {
|
||||||
const credential = this.node.credentials?.[credentialType];
|
const credential = props.node.credentials?.[credentialType];
|
||||||
assert(credential?.id);
|
assert(credential?.id);
|
||||||
|
|
||||||
this.uiStore.openExistingCredential(credential.id);
|
uiStore.openExistingCredential(credential.id);
|
||||||
|
|
||||||
this.$telemetry.track('User opened Credential modal', {
|
telemetry.track('User opened Credential modal', {
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
source: 'node',
|
source: 'node',
|
||||||
new_credential: false,
|
new_credential: false,
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: workflowsStore.workflowId,
|
||||||
});
|
});
|
||||||
this.subscribedToCredentialType = credentialType;
|
subscribedToCredentialType.value = credentialType;
|
||||||
},
|
}
|
||||||
showMixedCredentials(credentialType: INodeCredentialDescription): boolean {
|
|
||||||
const nodeType = this.nodeType;
|
|
||||||
const isRequired = isRequiredCredential(nodeType, credentialType);
|
|
||||||
|
|
||||||
return !KEEP_AUTH_IN_NDV_FOR_NODES.includes(this.node.type || '') && isRequired;
|
function showMixedCredentials(credentialType: INodeCredentialDescription): boolean {
|
||||||
},
|
const isRequired = isRequiredCredential(nodeType.value, credentialType);
|
||||||
getCredentialsFieldLabel(credentialType: INodeCredentialDescription): string {
|
|
||||||
|
return !KEEP_AUTH_IN_NDV_FOR_NODES.includes(props.node.type ?? '') && isRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCredentialsFieldLabel(credentialType: INodeCredentialDescription): string {
|
||||||
if (credentialType.displayName) return credentialType.displayName;
|
if (credentialType.displayName) return credentialType.displayName;
|
||||||
const credentialTypeName = this.credentialTypeNames[credentialType.name];
|
const credentialTypeName = credentialTypeNames.value[credentialType.name];
|
||||||
const isCredentialOnlyNode = this.node.type.startsWith(CREDENTIAL_ONLY_NODE_PREFIX);
|
const isCredentialOnlyNode = props.node.type.startsWith(CREDENTIAL_ONLY_NODE_PREFIX);
|
||||||
|
|
||||||
if (isCredentialOnlyNode) {
|
if (isCredentialOnlyNode) {
|
||||||
return this.$locale.baseText('nodeCredentials.credentialFor', {
|
return i18n.baseText('nodeCredentials.credentialFor', {
|
||||||
interpolate: { credentialType: this.nodeType?.displayName ?? credentialTypeName },
|
interpolate: { credentialType: nodeType.value?.displayName ?? credentialTypeName },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.showMixedCredentials(credentialType)) {
|
if (!showMixedCredentials(credentialType)) {
|
||||||
return this.$locale.baseText('nodeCredentials.credentialFor', {
|
return i18n.baseText('nodeCredentials.credentialFor', {
|
||||||
interpolate: { credentialType: credentialTypeName },
|
interpolate: { credentialType: credentialTypeName },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this.$locale.baseText('nodeCredentials.credentialsLabel');
|
return i18n.baseText('nodeCredentials.credentialsLabel');
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -516,7 +485,7 @@ export default defineComponent({
|
||||||
v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed"
|
v-for="credentialTypeDescription in credentialTypesNodeDescriptionDisplayed"
|
||||||
:key="credentialTypeDescription.name"
|
:key="credentialTypeDescription.name"
|
||||||
>
|
>
|
||||||
<n8n-input-label
|
<N8nInputLabel
|
||||||
:label="getCredentialsFieldLabel(credentialTypeDescription)"
|
:label="getCredentialsFieldLabel(credentialTypeDescription)"
|
||||||
:bold="false"
|
:bold="false"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -524,7 +493,7 @@ export default defineComponent({
|
||||||
data-test-id="credentials-label"
|
data-test-id="credentials-label"
|
||||||
>
|
>
|
||||||
<div v-if="readonly">
|
<div v-if="readonly">
|
||||||
<n8n-input
|
<N8nInput
|
||||||
:model-value="getSelectedName(credentialTypeDescription.name)"
|
:model-value="getSelectedName(credentialTypeDescription.name)"
|
||||||
disabled
|
disabled
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -540,7 +509,7 @@ export default defineComponent({
|
||||||
"
|
"
|
||||||
data-test-id="node-credentials-select"
|
data-test-id="node-credentials-select"
|
||||||
>
|
>
|
||||||
<n8n-select
|
<N8nSelect
|
||||||
:model-value="getSelectedId(credentialTypeDescription.name)"
|
:model-value="getSelectedId(credentialTypeDescription.name)"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
getSelectPlaceholder(
|
getSelectPlaceholder(
|
||||||
|
@ -557,9 +526,9 @@ export default defineComponent({
|
||||||
showMixedCredentials(credentialTypeDescription),
|
showMixedCredentials(credentialTypeDescription),
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
@blur="$emit('blur', 'credentials')"
|
@blur="emit('blur', 'credentials')"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="item in getCredentialOptions(
|
v-for="item in getCredentialOptions(
|
||||||
getAllRelatedCredentialTypes(credentialTypeDescription),
|
getAllRelatedCredentialTypes(credentialTypeDescription),
|
||||||
)"
|
)"
|
||||||
|
@ -569,24 +538,24 @@ export default defineComponent({
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
>
|
>
|
||||||
<div :class="[$style.credentialOption, 'mt-2xs', 'mb-2xs']">
|
<div :class="[$style.credentialOption, 'mt-2xs', 'mb-2xs']">
|
||||||
<n8n-text bold>{{ item.name }}</n8n-text>
|
<N8nText bold>{{ item.name }}</N8nText>
|
||||||
<n8n-text size="small">{{ item.typeDisplayName }}</n8n-text>
|
<N8nText size="small">{{ item.typeDisplayName }}</N8nText>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</N8nOption>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
:key="NEW_CREDENTIALS_TEXT"
|
:key="NEW_CREDENTIALS_TEXT"
|
||||||
data-test-id="node-credentials-select-item-new"
|
data-test-id="node-credentials-select-item-new"
|
||||||
:value="NEW_CREDENTIALS_TEXT"
|
:value="NEW_CREDENTIALS_TEXT"
|
||||||
:label="NEW_CREDENTIALS_TEXT"
|
:label="NEW_CREDENTIALS_TEXT"
|
||||||
>
|
>
|
||||||
</n8n-option>
|
</N8nOption>
|
||||||
</n8n-select>
|
</N8nSelect>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="getIssues(credentialTypeDescription.name).length && !hideIssues"
|
v-if="getIssues(credentialTypeDescription.name).length && !hideIssues"
|
||||||
:class="$style.warning"
|
:class="$style.warning"
|
||||||
>
|
>
|
||||||
<n8n-tooltip placement="top">
|
<N8nTooltip placement="top">
|
||||||
<template #content>
|
<template #content>
|
||||||
<TitledList
|
<TitledList
|
||||||
:title="`${$locale.baseText('nodeCredentials.issues')}:`"
|
:title="`${$locale.baseText('nodeCredentials.issues')}:`"
|
||||||
|
@ -594,7 +563,7 @@ export default defineComponent({
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<font-awesome-icon icon="exclamation-triangle" />
|
<font-awesome-icon icon="exclamation-triangle" />
|
||||||
</n8n-tooltip>
|
</N8nTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -613,7 +582,7 @@ export default defineComponent({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</N8nInputLabel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in a new issue