mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(editor): Make inputs dragged to Python Code editor produce working code (#19415)
This commit is contained in:
@@ -20,6 +20,7 @@ import { useLinter } from './linter';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { dropInCodeEditor } from '@/plugins/codemirror/dragAndDrop';
|
||||
import type { TargetNodeParameterContext } from '@/Interface';
|
||||
import { valueToInsert } from './utils';
|
||||
|
||||
export type CodeNodeLanguageOption = CodeNodeEditorLanguage | 'pythonNative';
|
||||
|
||||
@@ -203,12 +204,11 @@ function onAiLoadEnd() {
|
||||
async function onDrop(value: string, event: MouseEvent) {
|
||||
if (!editor.value) return;
|
||||
|
||||
const valueToInsert =
|
||||
props.mode === 'runOnceForAllItems'
|
||||
? value.replace('$json', '$input.first().json').replace(/\$\((.*)\)\.item/, '$($1).first()')
|
||||
: value;
|
||||
|
||||
await dropInCodeEditor(toRaw(editor.value), event, valueToInsert);
|
||||
await dropInCodeEditor(
|
||||
toRaw(editor.value),
|
||||
event,
|
||||
valueToInsert(value, props.language, props.mode),
|
||||
);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as esprima from 'esprima-next';
|
||||
import { walk } from './utils';
|
||||
import { valueToInsert, walk } from './utils';
|
||||
|
||||
describe('CodeNodeEditor utils', () => {
|
||||
describe('walk', () => {
|
||||
@@ -41,4 +41,88 @@ const y = f({ a: 'c' })
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueToInsert', () => {
|
||||
describe('JavaScript', () => {
|
||||
it('should convert input item correctly, runOnceForAllItems', () => {
|
||||
expect(
|
||||
valueToInsert('{{ $json.foo.bar[0].baz }}', 'javaScript', 'runOnceForAllItems'),
|
||||
).toBe('{{ $input.first().json.foo.bar[0].baz }}');
|
||||
});
|
||||
|
||||
it('should convert input item correctly, runOnceForEachItem', () => {
|
||||
expect(
|
||||
valueToInsert('{{ $json.foo.bar[0].baz }}', 'javaScript', 'runOnceForEachItem'),
|
||||
).toBe('{{ $json.foo.bar[0].baz }}');
|
||||
});
|
||||
|
||||
it('should convert previous node correctly, runOnceForAllItems', () => {
|
||||
expect(
|
||||
valueToInsert(
|
||||
"{{ $('Some Previous Node').item.json.foo.bar[0].baz }}",
|
||||
'javaScript',
|
||||
'runOnceForAllItems',
|
||||
),
|
||||
).toBe("{{ $('Some Previous Node').first().json.foo.bar[0].baz }}");
|
||||
});
|
||||
|
||||
it('should convert previous node correctly, runOnceForEachItem', () => {
|
||||
expect(
|
||||
valueToInsert(
|
||||
"{{ $('Some Previous Node').item.json.foo.bar[0].baz }}",
|
||||
'javaScript',
|
||||
'runOnceForEachItem',
|
||||
),
|
||||
).toBe("{{ $('Some Previous Node').item.json.foo.bar[0].baz }}");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Python (Pyodide)', () => {
|
||||
it('should convert input item correctly, runOnceForAllItems', () => {
|
||||
expect(valueToInsert('{{ $json.foo.bar[0].baz }}', 'python', 'runOnceForAllItems')).toBe(
|
||||
'{{ _input.first().json.foo.bar[0].baz }}',
|
||||
);
|
||||
});
|
||||
|
||||
it('should convert input item correctly, runOnceForEachItem', () => {
|
||||
expect(valueToInsert('{{ $json.foo.bar[0].baz }}', 'python', 'runOnceForEachItem')).toBe(
|
||||
'{{ _input.item.json.foo.bar[0].baz }}',
|
||||
);
|
||||
});
|
||||
|
||||
it('should convert previous node correctly, runOnceForAllItems', () => {
|
||||
expect(
|
||||
valueToInsert(
|
||||
"{{ $('Some Previous Node').item.json.foo.bar[0].baz }}",
|
||||
'python',
|
||||
'runOnceForAllItems',
|
||||
),
|
||||
).toBe("{{ _('Some Previous Node').first().json.foo.bar[0].baz }}");
|
||||
});
|
||||
|
||||
it('should convert previous node correctly, runOnceForEachItem', () => {
|
||||
expect(
|
||||
valueToInsert(
|
||||
"{{ $('Some Previous Node').item.json.foo.bar[0].baz }}",
|
||||
'python',
|
||||
'runOnceForEachItem',
|
||||
),
|
||||
).toBe("{{ _('Some Previous Node').item.json.foo.bar[0].baz }}");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Python (Native)', () => {
|
||||
it('should convert input item correctly, runOnceForAllItems', () => {
|
||||
expect(
|
||||
valueToInsert('{{ $json.foo.bar[0].baz }}', 'pythonNative', 'runOnceForAllItems'),
|
||||
).toBe('{{ _items[0]["json"]["foo"]["bar"][0]["baz"] }}');
|
||||
});
|
||||
|
||||
it('should convert input item correctly, runOnceForEachItem', () => {
|
||||
expect(
|
||||
valueToInsert('{{ $json.foo.bar[0].baz }}', 'pythonNative', 'runOnceForEachItem'),
|
||||
).toBe('{{ _item["json"]["foo"]["bar"][0]["baz"] }}');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { Completion } from '@codemirror/autocomplete';
|
||||
import type { RangeNode } from './types';
|
||||
import { sanitizeHtml } from '@/utils/htmlUtils';
|
||||
import type { Node } from 'estree';
|
||||
import type { CodeNodeLanguageOption } from './CodeNodeEditor.vue';
|
||||
import type { CodeExecutionMode } from 'n8n-workflow';
|
||||
|
||||
export function walk<T extends RangeNode>(
|
||||
node: Node | esprima.Program,
|
||||
@@ -57,3 +59,45 @@ export const addInfoRenderer = (option: Completion): Completion => {
|
||||
}
|
||||
return option;
|
||||
};
|
||||
|
||||
const DOT_CHAINS = /((?:\.[A-Za-z_$][A-Za-z0-9_$]*)+)/g;
|
||||
const DOT_KEY = /\.(?<key>[A-Za-z_$][A-Za-z0-9_$]*)/g;
|
||||
|
||||
// Convert dot notation ".a.b.c" chains -> ["a"]["b"]["c"]
|
||||
const toBracketNotation = (input: string): string => {
|
||||
return input.replace(DOT_CHAINS, (chain) => chain.replace(DOT_KEY, '["$<key>"]'));
|
||||
};
|
||||
|
||||
const pythonInsert = (value: string, mode: CodeExecutionMode): string => {
|
||||
const base =
|
||||
mode === 'runOnceForAllItems'
|
||||
? value.replace('$json', '_items[0]["json"]')
|
||||
: value.replace('$json', '_item["json"]');
|
||||
|
||||
return toBracketNotation(base);
|
||||
};
|
||||
|
||||
const pyodideInsert = (value: string, mode: CodeExecutionMode): string => {
|
||||
return value
|
||||
.replace('$json', mode === 'runOnceForAllItems' ? '_input.first().json' : '_input.item.json')
|
||||
.replace(/\$\((.*)\)\.item/, mode === 'runOnceForAllItems' ? '_($1).first()' : '_($1).item');
|
||||
};
|
||||
|
||||
const jsInsertForAllItems = (value: string): string => {
|
||||
return value.replace('$json', '$input.first().json').replace(/\$\((.*)\)\.item/, '$($1).first()');
|
||||
};
|
||||
|
||||
const isPyodide = (language: CodeNodeLanguageOption) => language === 'python';
|
||||
const isPython = (language: CodeNodeLanguageOption) => language === 'pythonNative';
|
||||
|
||||
export const valueToInsert = (
|
||||
value: string,
|
||||
language: CodeNodeLanguageOption,
|
||||
mode: CodeExecutionMode,
|
||||
): string => {
|
||||
if (isPython(language)) return pythonInsert(value, mode);
|
||||
if (isPyodide(language)) return pyodideInsert(value, mode);
|
||||
if (mode === 'runOnceForAllItems') return jsInsertForAllItems(value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user