feat: External Secrets storage for credentials (#6477)

Github issue / Community forum post (link here to close automatically):

---------

Co-authored-by: Romain Minaud <romain.minaud@gmail.com>
Co-authored-by: Valya Bullions <valya@n8n.io>
Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
Alex Grozav
2023-08-25 11:33:46 +03:00
committed by GitHub
parent c833078c87
commit ed927d34b2
89 changed files with 4164 additions and 57 deletions

View File

@@ -172,6 +172,7 @@ export const completerExtension = defineComponent({
if (value === '$execution') return this.executionCompletions(context, variable);
if (value === '$vars') return this.variablesCompletions(context, variable);
if (value === '$workflow') return this.workflowCompletions(context, variable);
if (value === '$prevNode') return this.prevNodeCompletions(context, variable);

View File

@@ -0,0 +1,54 @@
import Vue from 'vue';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { useExternalSecretsStore } from '@/stores';
const escape = (str: string) => str.replace('$', '\\$');
export const secretsCompletions = (Vue as CodeNodeEditorMixin).extend({
methods: {
/**
* Complete `$secrets.` to `$secrets.providerName` and `$secrets.providerName.secretName`.
*/
secretsCompletions(context: CompletionContext, matcher = '$secrets'): CompletionResult | null {
const pattern = new RegExp(`${escape(matcher)}\..*`);
const preCursor = context.matchBefore(pattern);
if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;
const provider = preCursor.text.split('.')[1];
const externalSecretsStore = useExternalSecretsStore();
let options: Completion[];
const optionsForObject = (leftSide: string, object: object): Completion[] => {
return Object.entries(object).flatMap(([key, value]) => {
if (typeof value === 'object' && value !== null) {
return optionsForObject(`${leftSide}.${key}`, value);
}
return {
label: `${leftSide}.${key}`,
info: '*******',
};
});
};
if (provider) {
options = optionsForObject(
`${matcher}.${provider}`,
externalSecretsStore.secretsAsObject[provider],
);
} else {
options = Object.keys(externalSecretsStore.secretsAsObject).map((provider) => ({
label: `${matcher}.${provider}`,
info: JSON.stringify(externalSecretsStore.secretsAsObject[provider]),
}));
}
return {
from: preCursor.from,
options: options.map(addVarType),
};
},
},
});

View File

@@ -8,7 +8,7 @@ const escape = (str: string) => str.replace('$', '\\$');
export const variablesCompletions = defineComponent({
methods: {
/**
* Complete `$workflow.` to `.id .name .active`.
* Complete `$vars.` to `$vars.VAR_NAME`.
*/
variablesCompletions(context: CompletionContext, matcher = '$vars'): CompletionResult | null {
const pattern = new RegExp(`${escape(matcher)}\..*`);