mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +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 { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { dropInCodeEditor } from '@/plugins/codemirror/dragAndDrop';
|
import { dropInCodeEditor } from '@/plugins/codemirror/dragAndDrop';
|
||||||
import type { TargetNodeParameterContext } from '@/Interface';
|
import type { TargetNodeParameterContext } from '@/Interface';
|
||||||
|
import { valueToInsert } from './utils';
|
||||||
|
|
||||||
export type CodeNodeLanguageOption = CodeNodeEditorLanguage | 'pythonNative';
|
export type CodeNodeLanguageOption = CodeNodeEditorLanguage | 'pythonNative';
|
||||||
|
|
||||||
@@ -203,12 +204,11 @@ function onAiLoadEnd() {
|
|||||||
async function onDrop(value: string, event: MouseEvent) {
|
async function onDrop(value: string, event: MouseEvent) {
|
||||||
if (!editor.value) return;
|
if (!editor.value) return;
|
||||||
|
|
||||||
const valueToInsert =
|
await dropInCodeEditor(
|
||||||
props.mode === 'runOnceForAllItems'
|
toRaw(editor.value),
|
||||||
? value.replace('$json', '$input.first().json').replace(/\$\((.*)\)\.item/, '$($1).first()')
|
event,
|
||||||
: value;
|
valueToInsert(value, props.language, props.mode),
|
||||||
|
);
|
||||||
await dropInCodeEditor(toRaw(editor.value), event, valueToInsert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as esprima from 'esprima-next';
|
import * as esprima from 'esprima-next';
|
||||||
import { walk } from './utils';
|
import { valueToInsert, walk } from './utils';
|
||||||
|
|
||||||
describe('CodeNodeEditor utils', () => {
|
describe('CodeNodeEditor utils', () => {
|
||||||
describe('walk', () => {
|
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 type { RangeNode } from './types';
|
||||||
import { sanitizeHtml } from '@/utils/htmlUtils';
|
import { sanitizeHtml } from '@/utils/htmlUtils';
|
||||||
import type { Node } from 'estree';
|
import type { Node } from 'estree';
|
||||||
|
import type { CodeNodeLanguageOption } from './CodeNodeEditor.vue';
|
||||||
|
import type { CodeExecutionMode } from 'n8n-workflow';
|
||||||
|
|
||||||
export function walk<T extends RangeNode>(
|
export function walk<T extends RangeNode>(
|
||||||
node: Node | esprima.Program,
|
node: Node | esprima.Program,
|
||||||
@@ -57,3 +59,45 @@ export const addInfoRenderer = (option: Completion): Completion => {
|
|||||||
}
|
}
|
||||||
return option;
|
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