mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(Code Node): Consistent redirection of stdout for JS and Python sandboxes (#6818)
Co-authored-by: Marcus <marcus@n8n.io>
This commit is contained in:
parent
34df8b6238
commit
f718c2291f
|
@ -92,8 +92,9 @@ export class Code implements INodeType {
|
|||
const nodeMode = this.getNodeParameter('mode', 0) as CodeExecutionMode;
|
||||
const workflowMode = this.getMode();
|
||||
|
||||
const node = this.getNode();
|
||||
const language: CodeNodeEditorLanguage =
|
||||
this.getNode()?.typeVersion === 2
|
||||
node.typeVersion === 2
|
||||
? (this.getNodeParameter('language', 0) as CodeNodeEditorLanguage)
|
||||
: 'javaScript';
|
||||
const codeParameterName = language === 'python' ? 'pythonCode' : 'jsCode';
|
||||
|
@ -107,16 +108,16 @@ export class Code implements INodeType {
|
|||
context.item = context.$input.item;
|
||||
}
|
||||
|
||||
if (language === 'python') {
|
||||
context.printOverwrite = workflowMode === 'manual' ? this.sendMessageToUI : null;
|
||||
return new PythonSandbox(context, code, index, this.helpers);
|
||||
} else {
|
||||
const sandbox = new JavaScriptSandbox(context, code, index, workflowMode, this.helpers);
|
||||
if (workflowMode === 'manual') {
|
||||
sandbox.vm.on('console.log', this.sendMessageToUI);
|
||||
}
|
||||
return sandbox;
|
||||
}
|
||||
const Sandbox = language === 'python' ? PythonSandbox : JavaScriptSandbox;
|
||||
const sandbox = new Sandbox(context, code, index, this.helpers);
|
||||
sandbox.on(
|
||||
'output',
|
||||
workflowMode === 'manual'
|
||||
? this.sendMessageToUI
|
||||
: (...args) =>
|
||||
console.log(`[Workflow "${this.getWorkflow().id}"][Node "${node.name}"]`, ...args),
|
||||
);
|
||||
return sandbox;
|
||||
};
|
||||
|
||||
// ----------------------------------
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { NodeVMOptions } from 'vm2';
|
||||
import { NodeVM, makeResolverFromLegacyOptions } from 'vm2';
|
||||
import type { IExecuteFunctions, INodeExecutionData, WorkflowExecuteMode } from 'n8n-workflow';
|
||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
import { ValidationError } from './ValidationError';
|
||||
import { ExecutionError } from './ExecutionError';
|
||||
|
@ -20,23 +19,13 @@ export const vmResolver = makeResolverFromLegacyOptions({
|
|||
builtin: builtIn?.split(',') ?? [],
|
||||
});
|
||||
|
||||
const getSandboxOptions = (
|
||||
context: SandboxContext,
|
||||
workflowMode: WorkflowExecuteMode,
|
||||
): NodeVMOptions => ({
|
||||
console: workflowMode === 'manual' ? 'redirect' : 'inherit',
|
||||
sandbox: context,
|
||||
require: vmResolver,
|
||||
});
|
||||
|
||||
export class JavaScriptSandbox extends Sandbox {
|
||||
readonly vm: NodeVM;
|
||||
private readonly vm: NodeVM;
|
||||
|
||||
constructor(
|
||||
context: SandboxContext,
|
||||
private jsCode: string,
|
||||
itemIndex: number | undefined,
|
||||
workflowMode: WorkflowExecuteMode,
|
||||
helpers: IExecuteFunctions['helpers'],
|
||||
) {
|
||||
super(
|
||||
|
@ -49,7 +38,13 @@ export class JavaScriptSandbox extends Sandbox {
|
|||
itemIndex,
|
||||
helpers,
|
||||
);
|
||||
this.vm = new NodeVM(getSandboxOptions(context, workflowMode));
|
||||
this.vm = new NodeVM({
|
||||
console: 'redirect',
|
||||
sandbox: context,
|
||||
require: vmResolver,
|
||||
});
|
||||
|
||||
this.vm.on('console.log', (...args: unknown[]) => this.emit('output', ...args));
|
||||
}
|
||||
|
||||
async runCodeAllItems(): Promise<INodeExecutionData[]> {
|
||||
|
|
|
@ -67,10 +67,7 @@ export class PythonSandbox extends Sandbox {
|
|||
globalsDict.set(key, value);
|
||||
}
|
||||
|
||||
await pyodide.runPythonAsync(`
|
||||
if 'printOverwrite' in globals():
|
||||
print = printOverwrite
|
||||
`);
|
||||
pyodide.setStdout({ batched: (str) => this.emit('output', str) });
|
||||
|
||||
const runCode = `
|
||||
async def __main():
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { EventEmitter } from 'events';
|
||||
import { ValidationError } from './ValidationError';
|
||||
import { isObject } from './utils';
|
||||
|
||||
|
@ -31,12 +32,14 @@ export function getSandboxContext(this: IExecuteFunctions, index: number): Sandb
|
|||
};
|
||||
}
|
||||
|
||||
export abstract class Sandbox {
|
||||
export abstract class Sandbox extends EventEmitter {
|
||||
constructor(
|
||||
private textKeys: SandboxTextKeys,
|
||||
protected itemIndex: number | undefined,
|
||||
protected helpers: IExecuteFunctions['helpers'],
|
||||
) {}
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
abstract runCodeAllItems(): Promise<INodeExecutionData[]>;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ describe('Test Code Node', () => {
|
|||
describe('Code Node unit test', () => {
|
||||
const node = new Code();
|
||||
const thisArg = mock<IExecuteFunctions>({
|
||||
getNode: () => mock(),
|
||||
helpers: { normalizeItems },
|
||||
prepareOutputData: NodeHelpers.prepareOutputData,
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue