mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Escape node names with quotes in autocomplete and drag'n'drop (#8663)
This commit is contained in:
parent
c346002cc6
commit
890c2bd52b
|
@ -5,6 +5,7 @@ import type { Completion, CompletionContext, CompletionResult } from '@codemirro
|
|||
import type { INodeUi } from '@/Interface';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { escapeMappingString } from '@/utils/mappingUtils';
|
||||
|
||||
function getAutoCompletableNodeNames(nodes: INodeUi[]) {
|
||||
return nodes
|
||||
|
@ -98,7 +99,7 @@ export const baseCompletions = defineComponent({
|
|||
options.push(
|
||||
...getAutoCompletableNodeNames(this.workflowsStore.allNodes).map((nodeName) => {
|
||||
return {
|
||||
label: `${prefix}('${nodeName}')`,
|
||||
label: `${prefix}('${escapeMappingString(nodeName)}')`,
|
||||
type: 'variable',
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$()', {
|
||||
interpolate: { nodeName },
|
||||
|
@ -138,7 +139,7 @@ export const baseCompletions = defineComponent({
|
|||
const options: Completion[] = getAutoCompletableNodeNames(this.workflowsStore.allNodes).map(
|
||||
(nodeName) => {
|
||||
return {
|
||||
label: `${prefix}('${nodeName}')`,
|
||||
label: `${prefix}('${escapeMappingString(nodeName)}')`,
|
||||
type: 'variable',
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$()', {
|
||||
interpolate: { nodeName },
|
||||
|
|
|
@ -50,6 +50,7 @@ import { useRootStore } from '@/stores/n8nRoot.store';
|
|||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { escapeMappingString } from '@/utils/mappingUtils';
|
||||
|
||||
// Node types that should not be displayed in variable selector
|
||||
const SKIPPED_NODE_TYPES = [STICKY_NODE_TYPE];
|
||||
|
@ -398,7 +399,9 @@ export default defineComponent({
|
|||
|
||||
// Get json data
|
||||
if (outputData.hasOwnProperty('json')) {
|
||||
const jsonPropertyPrefix = useShort ? '$json' : `$('${nodeName}').item.json`;
|
||||
const jsonPropertyPrefix = useShort
|
||||
? '$json'
|
||||
: `$('${escapeMappingString(nodeName)}').item.json`;
|
||||
|
||||
const jsonDataOptions: IVariableSelectorOption[] = [];
|
||||
for (const propertyName of Object.keys(outputData.json)) {
|
||||
|
@ -423,7 +426,9 @@ export default defineComponent({
|
|||
|
||||
// Get binary data
|
||||
if (outputData.hasOwnProperty('binary')) {
|
||||
const binaryPropertyPrefix = useShort ? '$binary' : `$('${nodeName}').item.binary`;
|
||||
const binaryPropertyPrefix = useShort
|
||||
? '$binary'
|
||||
: `$('${escapeMappingString(nodeName)}').item.binary`;
|
||||
|
||||
const binaryData = [];
|
||||
let binaryPropertyData = [];
|
||||
|
@ -537,7 +542,7 @@ export default defineComponent({
|
|||
|
||||
returnData.push({
|
||||
name: key,
|
||||
key: `$('${nodeName}').context["${key}"]`,
|
||||
key: `$('${escapeMappingString(nodeName)}').context['${escapeMappingString(key)}']`,
|
||||
// @ts-ignore
|
||||
value: nodeContext[key],
|
||||
});
|
||||
|
@ -793,7 +798,12 @@ export default defineComponent({
|
|||
{
|
||||
name: this.$locale.baseText('variableSelector.parameters'),
|
||||
options: this.sortOptions(
|
||||
this.getNodeParameters(nodeName, `$('${nodeName}').params`, undefined, filterText),
|
||||
this.getNodeParameters(
|
||||
nodeName,
|
||||
`$('${escapeMappingString(nodeName)}').params`,
|
||||
undefined,
|
||||
filterText,
|
||||
),
|
||||
),
|
||||
} as IVariableSelectorOption,
|
||||
];
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from './utils';
|
||||
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
||||
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
|
||||
import { escapeMappingString } from '@/utils/mappingUtils';
|
||||
|
||||
/**
|
||||
* Completions offered at the dollar position: `$|`
|
||||
|
@ -90,7 +91,7 @@ export function dollarOptions() {
|
|||
})
|
||||
.concat(
|
||||
autocompletableNodeNames().map((nodeName) => ({
|
||||
label: `$('${nodeName}')`,
|
||||
label: `$('${escapeMappingString(nodeName)}')`,
|
||||
type: 'keyword',
|
||||
info: i18n.baseText('codeNodeEditor.completer.$()', { interpolate: { nodeName } }),
|
||||
})),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { getMappedResult, getMappedExpression } from '../mappingUtils';
|
||||
import { getMappedResult, getMappedExpression, escapeMappingString } from '../mappingUtils';
|
||||
|
||||
const RLC_PARAM: INodeProperties = {
|
||||
displayName: 'Base',
|
||||
|
@ -273,4 +273,12 @@ describe('Mapping Utils', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
describe('escapeMappingString', () => {
|
||||
test.each([
|
||||
{ input: 'Normal node name (here)', output: 'Normal node name (here)' },
|
||||
{ input: "'Should es'ape quotes here'", output: "\\'Should es\\'ape quotes here\\'" },
|
||||
])('should escape "$input" to "$output"', ({ input, output }) => {
|
||||
expect(escapeMappingString(input)).toEqual(output);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,6 +18,10 @@ export function generatePath(root: string, path: Array<string | number>): string
|
|||
}, root);
|
||||
}
|
||||
|
||||
export function escapeMappingString(str: string): string {
|
||||
return str.replace(/\'/g, "\\'");
|
||||
}
|
||||
|
||||
export function getMappedExpression({
|
||||
nodeName,
|
||||
distanceFromActive,
|
||||
|
@ -28,7 +32,9 @@ export function getMappedExpression({
|
|||
path: Array<string | number> | string;
|
||||
}) {
|
||||
const root =
|
||||
distanceFromActive === 1 ? '$json' : generatePath(`$('${nodeName}')`, ['item', 'json']);
|
||||
distanceFromActive === 1
|
||||
? '$json'
|
||||
: generatePath(`$('${escapeMappingString(nodeName)}')`, ['item', 'json']);
|
||||
|
||||
if (typeof path === 'string') {
|
||||
return `{{ ${root}${path} }}`;
|
||||
|
|
Loading…
Reference in a new issue