fix: Overhaul expression error messages related to paired item (#8765)

This commit is contained in:
Elias Meire
2024-03-21 16:59:22 +01:00
committed by GitHub
parent 079a1147d4
commit 45461c8cb5
6 changed files with 135 additions and 102 deletions

View File

@@ -271,9 +271,11 @@ export class WorkflowDataProxy {
}
if (!that.workflow.getNode(nodeName)) {
throw new ExpressionError(`"${nodeName}" node doesn't exist`, {
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
@@ -281,10 +283,11 @@ export class WorkflowDataProxy {
!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName) &&
!that.workflow.getPinDataOfNode(nodeName)
) {
throw new ExpressionError(`no data, execute "${nodeName}" node first`, {
throw new ExpressionError('Referenced node is unexecuted', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: 'no_node_execution_data',
descriptionKey: 'noNodeExecutionData',
nodeCause: nodeName,
});
}
@@ -369,7 +372,12 @@ export class WorkflowDataProxy {
name = name.toString();
if (!node) {
throw new ExpressionError(`"${nodeName}" node doesn't exist`);
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
if (['binary', 'data', 'json'].includes(name)) {
@@ -380,8 +388,7 @@ export class WorkflowDataProxy {
throw new ExpressionError('No execution data available', {
messageTemplate:
'No execution data available to expression under %%PARAMETER%%',
description:
'This node has no input data. Please make sure this node is connected to another node.',
descriptionKey: 'noInputConnection',
nodeCause: nodeName,
runIndex: that.runIndex,
itemIndex: that.itemIndex,
@@ -589,9 +596,11 @@ export class WorkflowDataProxy {
const nodeName = name.toString();
if (that.workflow.getNode(nodeName) === null) {
throw new ExpressionError(`"${nodeName}" node doesn't exist`, {
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
@@ -666,10 +675,9 @@ export class WorkflowDataProxy {
if (!context) {
context = {};
}
message = `Node ${nodeName} must be unpinned to execute`;
message = `Unpin '${nodeName}' to execute`;
context.messageTemplate = undefined;
context.description = `To fetch the data for the expression, you must unpin the node <strong>'${nodeName}'</strong> and execute the workflow again.`;
context.descriptionTemplate = `To fetch the data for the expression under '%%PARAMETER%%', you must unpin the node <strong>'${nodeName}'</strong> and execute the workflow again.`;
context.descriptionKey = 'pairedItemPinned';
}
if (context.moreInfoLink && (pinData || isScriptingNode(nodeName, that.workflow))) {
@@ -688,6 +696,48 @@ export class WorkflowDataProxy {
});
};
const createInvalidPairedItemError = ({ nodeName }: { nodeName: string }) => {
return createExpressionError("Can't get data for expression", {
messageTemplate: 'Expression info invalid',
functionality: 'pairedItem',
functionOverrides: {
message: "Can't get data",
},
nodeCause: nodeName,
descriptionKey: 'pairedItemInvalidInfo',
type: 'paired_item_invalid_info',
});
};
const createMissingPairedItemError = (nodeCause: string) => {
return createExpressionError("Can't get data for expression", {
messageTemplate: 'Info for expression missing from previous node',
functionality: 'pairedItem',
functionOverrides: {
message: "Can't get data",
},
nodeCause,
descriptionKey: isScriptingNode(nodeCause, that.workflow)
? 'pairedItemNoInfoCodeNode'
: 'pairedItemNoInfo',
causeDetailed: `Missing pairedItem data (node '${nodeCause}' probably didn't supply it)`,
type: 'paired_item_no_info',
});
};
const createNoConnectionError = (nodeCause: string) => {
return createExpressionError('Invalid expression', {
messageTemplate: 'No path back to referenced node',
functionality: 'pairedItem',
descriptionKey: isScriptingNode(nodeCause, that.workflow)
? 'pairedItemNoConnectionCodeNode'
: 'pairedItemNoConnection',
type: 'paired_item_no_connection',
moreInfoLink: true,
nodeCause,
});
};
const getPairedItem = (
destinationNodeName: string,
incomingSourceData: ISourceData | null,
@@ -736,42 +786,15 @@ export class WorkflowDataProxy {
const source = taskData?.source ?? [];
if (pairedItem.item >= previousNodeOutputData.length) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionality: 'pairedItem',
functionOverrides: {
message: 'Cant get data',
},
nodeCause: nodeBeforeLast,
description: `In node <strong>${nodeBeforeLast!}</strong>, output item ${
currentPairedItem.item || 0
} ${
sourceData.previousNodeRun
? `of run ${(sourceData.previousNodeRun || 0).toString()} `
: ''
}points to an input item on node <strong>${
sourceData.previousNode
}</strong> that doesnt exist.`,
type: 'paired_item_invalid_info',
moreInfoLink: true,
throw createInvalidPairedItemError({
nodeName: sourceData.previousNode,
});
}
const itemPreviousNode: INodeExecutionData = previousNodeOutputData[pairedItem.item];
if (itemPreviousNode.pairedItem === undefined) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionality: 'pairedItem',
functionOverrides: {
message: 'Cant get data',
},
nodeCause: sourceData.previousNode,
description: `To fetch the data from other nodes that this expression needs, more information is needed from the node <strong>${sourceData.previousNode}</strong>`,
causeDetailed: `Missing pairedItem data (node ${sourceData.previousNode} probably didnt supply it)`,
type: 'paired_item_no_info',
moreInfoLink: true,
});
throw createMissingPairedItemError(sourceData.previousNode);
}
if (Array.isArray(itemPreviousNode.pairedItem)) {
@@ -804,13 +827,17 @@ export class WorkflowDataProxy {
}
throw createExpressionError('Invalid expression', {
messageTemplate: 'Invalid expression under %%PARAMETER%%',
messageTemplate: `Multiple matching items for expression [item ${
currentPairedItem.item || 0
}]`,
functionality: 'pairedItem',
functionOverrides: {
description: `The code uses data in the node <strong>${destinationNodeName}</strong> but there is more than one matching item in that node`,
message: 'Invalid code',
message: `Multiple matching items for code [item ${currentPairedItem.item || 0}]`,
},
description: `The expression uses data in the node <strong>${destinationNodeName}</strong> but there is more than one matching item in that node`,
nodeCause: destinationNodeName,
descriptionKey: isScriptingNode(destinationNodeName, that.workflow)
? 'pairedItemMultipleMatchesCodeNode'
: 'pairedItemMultipleMatches',
type: 'paired_item_multiple_matches',
});
}
@@ -833,17 +860,7 @@ export class WorkflowDataProxy {
if (itemInput >= source.length) {
if (source.length === 0) {
// A trigger node got reached, so looks like that that item can not be resolved
throw createExpressionError('Invalid expression', {
messageTemplate: 'Invalid expression under %%PARAMETER%%',
functionality: 'pairedItem',
functionOverrides: {
description: `The code uses data in the node <strong>${destinationNodeName}</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
message: 'Invalid code',
},
description: `The expression uses data in the node <strong>${destinationNodeName}</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
type: 'paired_item_no_connection',
moreInfoLink: true,
});
throw createNoConnectionError(destinationNodeName);
}
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
@@ -905,24 +922,8 @@ export class WorkflowDataProxy {
}
if (pairedItem.item >= taskData.data!.main[previousNodeOutput]!.length) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionality: 'pairedItem',
functionOverrides: {
message: 'Cant get data',
},
nodeCause: nodeBeforeLast,
description: `In node <strong>${nodeBeforeLast!}</strong>, output item ${
currentPairedItem.item || 0
} ${
sourceData.previousNodeRun
? `of run ${(sourceData.previousNodeRun || 0).toString()} `
: ''
}points to an input item on node <strong>${
sourceData.previousNode
}</strong> that doesnt exist.`,
type: 'paired_item_invalid_info',
moreInfoLink: true,
throw createInvalidPairedItemError({
nodeName: sourceData.previousNode,
});
}
@@ -937,7 +938,12 @@ export class WorkflowDataProxy {
const referencedNode = that.workflow.getNode(nodeName);
if (referencedNode === null) {
throw createExpressionError(`"${nodeName}" node doesn't exist`);
throw createExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
const ensureNodeExecutionData = () => {
@@ -945,8 +951,11 @@ export class WorkflowDataProxy {
!that?.runExecutionData?.resultData?.runData.hasOwnProperty(nodeName) &&
!that.workflow.getPinDataOfNode(nodeName)
) {
throw createExpressionError(`no data, execute "${nodeName}" node first`, {
throw createExpressionError('Referenced node is unexecuted', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: 'no_node_execution_data',
descriptionKey: 'noNodeExecutionData',
nodeCause: nodeName,
});
}
@@ -989,17 +998,7 @@ export class WorkflowDataProxy {
}
const parentNodes = that.workflow.getParentNodes(contextNode);
if (!parentNodes.includes(nodeName)) {
throw createExpressionError('Invalid expression', {
messageTemplate: 'Invalid expression under %%PARAMETER%%',
functionality: 'pairedItem',
functionOverrides: {
description: `The code uses data in the node <strong>${nodeName}</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
message: `No path back to node ${nodeName}`,
},
description: `The expression uses data in the node <strong>${nodeName}</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
nodeCause: nodeName,
type: 'paired_item_no_connection',
});
throw createNoConnectionError(nodeName);
}
ensureNodeExecutionData();
@@ -1037,17 +1036,7 @@ export class WorkflowDataProxy {
const pairedItem = input.pairedItem as IPairedItemData;
if (pairedItem === undefined) {
throw createExpressionError('Cant get data for expression', {
messageTemplate: 'Cant get data for expression under %%PARAMETER%% field',
functionality: 'pairedItem',
functionOverrides: {
description: `To fetch the data from other nodes that this code needs, more information is needed from the node <strong>${that.activeNodeName}</strong>`,
message: 'Cant get data',
},
description: `To fetch the data from other nodes that this expression needs, more information is needed from the node <strong>${that.activeNodeName}</strong>`,
causeDetailed: `Missing pairedItem data (node ${that.activeNodeName} probably didnt supply it)`,
itemIndex,
});
throw createMissingPairedItemError(that.activeNodeName);
}
if (!that.executeData?.source) {

View File

@@ -5,6 +5,7 @@ export interface ExpressionErrorOptions {
cause?: Error;
causeDetailed?: string;
description?: string;
descriptionKey?: string;
descriptionTemplate?: string;
functionality?: 'pairedItem';
itemIndex?: number;
@@ -38,6 +39,7 @@ export class ExpressionError extends ExecutionBaseError {
const allowedKeys = [
'causeDetailed',
'descriptionTemplate',
'descriptionKey',
'functionality',
'itemIndex',
'messageTemplate',

View File

@@ -162,7 +162,7 @@ describe('WorkflowDataProxy', () => {
} catch (error) {
expect(error).toBeInstanceOf(ExpressionError);
const exprError = error as ExpressionError;
expect(exprError.message).toEqual('"does not exist" node doesn\'t exist');
expect(exprError.message).toEqual("Referenced node doesn't exist");
done();
}
});
@@ -193,7 +193,7 @@ describe('WorkflowDataProxy', () => {
} catch (error) {
expect(error).toBeInstanceOf(ExpressionError);
const exprError = error as ExpressionError;
expect(exprError.message).toEqual('no data, execute "Impossible" node first');
expect(exprError.message).toEqual('Referenced node is unexecuted');
expect(exprError.context.type).toEqual('no_node_execution_data');
done();
}
@@ -221,7 +221,7 @@ describe('WorkflowDataProxy', () => {
} catch (error) {
expect(error).toBeInstanceOf(ExpressionError);
const exprError = error as ExpressionError;
expect(exprError.message).toEqual('no data, execute "Impossible if" node first');
expect(exprError.message).toEqual('Referenced node is unexecuted');
expect(exprError.context.type).toEqual('no_node_execution_data');
done();
}
@@ -263,7 +263,7 @@ describe('WorkflowDataProxy', () => {
} catch (error) {
expect(error).toBeInstanceOf(ExpressionError);
const exprError = error as ExpressionError;
expect(exprError.message).toEqual('Cant get data for expression');
expect(exprError.message).toEqual("Can't get data for expression");
expect(exprError.context.type).toEqual('paired_item_no_info');
done();
}
@@ -277,7 +277,7 @@ describe('WorkflowDataProxy', () => {
} catch (error) {
expect(error).toBeInstanceOf(ExpressionError);
const exprError = error as ExpressionError;
expect(exprError.message).toEqual('Cant get data for expression');
expect(exprError.message).toEqual("Can't get data for expression");
expect(exprError.context.type).toEqual('paired_item_invalid_info');
done();
}