refactor(Code Node): Constently handle various kinds of data returned by user code (#6002)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-04-19 13:09:46 +02:00
committed by GitHub
parent fe058aa8ee
commit f9b3aeac44
6 changed files with 309 additions and 220 deletions

View File

@@ -1462,21 +1462,30 @@ export interface IWebhookDescription {
restartWebhook?: boolean;
}
export interface ProxyInput {
all: () => INodeExecutionData[];
context: any;
first: () => INodeExecutionData | undefined;
item: INodeExecutionData | undefined;
last: () => INodeExecutionData | undefined;
params?: INodeParameters;
}
export interface IWorkflowDataProxyData {
[key: string]: any;
$binary: any;
$binary: INodeExecutionData['binary'];
$data: any;
$env: any;
$evaluateExpression: any;
$item: any;
$items: any;
$json: any;
$evaluateExpression: (expression: string, itemIndex?: number) => NodeParameterValueType;
$item: (itemIndex: number, runIndex?: number) => IWorkflowDataProxyData;
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => INodeExecutionData[];
$json: INodeExecutionData['json'];
$node: any;
$parameter: any;
$position: any;
$parameter: INodeParameters;
$position: number;
$workflow: any;
$: any;
$input: any;
$input: ProxyInput;
$thisItem: any;
$thisRunIndex: number;
$thisItemIndex: number;

View File

@@ -24,6 +24,7 @@ import type {
INodeParameterResourceLocator,
NodeParameterValueType,
WorkflowExecuteMode,
ProxyInput,
} from './Interfaces';
import * as NodeHelpers from './NodeHelpers';
import { ExpressionError } from './ExpressionError';
@@ -1067,90 +1068,87 @@ export class WorkflowDataProxy {
);
},
$input: new Proxy(
{},
{
ownKeys(target) {
return ['all', 'context', 'first', 'item', 'last', 'params'];
},
getOwnPropertyDescriptor(k) {
return {
enumerable: true,
configurable: true,
};
},
get(target, property, receiver) {
if (property === 'isProxy') return true;
if (property === 'item') {
return that.connectionInputData[that.itemIndex];
}
if (property === 'first') {
return (...args: unknown[]) => {
if (args.length) {
throw createExpressionError('$input.first() should have no arguments');
}
const result = that.connectionInputData;
if (result[0]) {
return result[0];
}
return undefined;
};
}
if (property === 'last') {
return (...args: unknown[]) => {
if (args.length) {
throw createExpressionError('$input.last() should have no arguments');
}
const result = that.connectionInputData;
if (result.length && result[result.length - 1]) {
return result[result.length - 1];
}
return undefined;
};
}
if (property === 'all') {
return () => {
const result = that.connectionInputData;
if (result.length) {
return result;
}
return [];
};
}
if (['context', 'params'].includes(property as string)) {
// For the following properties we need the source data so fail in case it is missing
// for some reason (even though that should actually never happen)
if (!that.executeData?.source) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionOverrides: {
message: 'Cant get data',
},
description:
'Apologies, this is an internal error. See details for more information',
causeDetailed: 'Missing sourceData (probably an internal error)',
runIndex: that.runIndex,
});
}
const sourceData: ISourceData = that.executeData.source.main[0] as ISourceData;
if (property === 'context') {
return that.nodeContextGetter(sourceData.previousNode);
}
if (property === 'params') {
return that.workflow.getNode(sourceData.previousNode)?.parameters;
}
}
return Reflect.get(target, property, receiver);
},
$input: new Proxy({} as ProxyInput, {
ownKeys(target) {
return ['all', 'context', 'first', 'item', 'last', 'params'];
},
),
getOwnPropertyDescriptor(k) {
return {
enumerable: true,
configurable: true,
};
},
get(target, property, receiver) {
if (property === 'isProxy') return true;
if (property === 'item') {
return that.connectionInputData[that.itemIndex];
}
if (property === 'first') {
return (...args: unknown[]) => {
if (args.length) {
throw createExpressionError('$input.first() should have no arguments');
}
const result = that.connectionInputData;
if (result[0]) {
return result[0];
}
return undefined;
};
}
if (property === 'last') {
return (...args: unknown[]) => {
if (args.length) {
throw createExpressionError('$input.last() should have no arguments');
}
const result = that.connectionInputData;
if (result.length && result[result.length - 1]) {
return result[result.length - 1];
}
return undefined;
};
}
if (property === 'all') {
return () => {
const result = that.connectionInputData;
if (result.length) {
return result;
}
return [];
};
}
if (['context', 'params'].includes(property as string)) {
// For the following properties we need the source data so fail in case it is missing
// for some reason (even though that should actually never happen)
if (!that.executeData?.source) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionOverrides: {
message: 'Cant get data',
},
description:
'Apologies, this is an internal error. See details for more information',
causeDetailed: 'Missing sourceData (probably an internal error)',
runIndex: that.runIndex,
});
}
const sourceData: ISourceData = that.executeData.source.main[0] as ISourceData;
if (property === 'context') {
return that.nodeContextGetter(sourceData.previousNode);
}
if (property === 'params') {
return that.workflow.getNode(sourceData.previousNode)?.parameters;
}
}
return Reflect.get(target, property, receiver);
},
}),
$binary: {}, // Placeholder
$data: {}, // Placeholder