feat: More hints to nodes (#10565)

Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com>
This commit is contained in:
Michael Kret
2024-09-04 16:33:10 +03:00
committed by GitHub
parent cd0891e4f1
commit 66ddb4a6f3
6 changed files with 325 additions and 3 deletions

View File

@@ -1,17 +1,27 @@
import { isNumber, isValidNodeConnectionType } from '@/utils/typeGuards';
import { NODE_OUTPUT_DEFAULT_KEY, STICKY_NODE_TYPE } from '@/constants';
import {
LIST_LIKE_NODE_OPERATIONS,
NODE_OUTPUT_DEFAULT_KEY,
SET_NODE_TYPE,
SPLIT_IN_BATCHES_NODE_TYPE,
STICKY_NODE_TYPE,
} from '@/constants';
import type { EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
import type { ArrayAnchorSpec, ConnectorSpec, OverlaySpec, PaintStyle } from '@jsplumb/common';
import type { Connection, Endpoint, SelectOptions } from '@jsplumb/core';
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
import type {
AssignmentCollectionValue,
IConnection,
INode,
INodeExecutionData,
INodeTypeDescription,
ITaskData,
NodeHint,
NodeInputConnections,
Workflow,
} from 'n8n-workflow';
import { NodeConnectionType } from 'n8n-workflow';
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
import type { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
import { EVENT_CONNECTION_MOUSEOUT, EVENT_CONNECTION_MOUSEOVER } from '@jsplumb/browser-ui';
import { useUIStore } from '@/stores/ui.store';
@@ -1173,3 +1183,109 @@ export const getJSPlumbConnection = (
return uuids[0] === sourceEndpoint && uuids[1] === targetEndpoint;
});
};
export function getGenericHints({
workflowNode,
node,
nodeType,
nodeOutputData,
hasMultipleInputItems,
workflow,
hasNodeRun,
}: {
workflowNode: INode;
node: INodeUi;
nodeType: INodeTypeDescription;
nodeOutputData: INodeExecutionData[];
hasMultipleInputItems: boolean;
workflow: Workflow;
hasNodeRun: boolean;
}) {
const nodeHints: NodeHint[] = [];
// add limit reached hint
if (hasNodeRun && workflowNode.parameters.limit) {
if (nodeOutputData.length === workflowNode.parameters.limit) {
nodeHints.push({
message: `Limit of ${workflowNode.parameters.limit as number} items reached. There may be more items that aren't being returned. Tweak the 'Return All' or 'Limit' parameters to access more items.`,
location: 'outputPane',
whenToDisplay: 'afterExecution',
});
}
}
// add Execute Once hint
if (
hasMultipleInputItems &&
LIST_LIKE_NODE_OPERATIONS.includes((workflowNode.parameters.operation as string) || '')
) {
const executeOnce = workflow.getNode(node.name)?.executeOnce;
if (!executeOnce) {
nodeHints.push({
message:
"The operation is performed for each input item. Use the 'Execute Once' setting to execute it only once for the first input item.",
location: 'outputPane',
});
}
}
// add expression in field name hint for Set node
if (node.type === SET_NODE_TYPE && node.parameters.mode === 'manual') {
const rawParameters = NodeHelpers.getNodeParameters(
nodeType.properties,
node.parameters,
true,
false,
node,
undefined,
false,
);
const assignments =
((rawParameters?.assignments as AssignmentCollectionValue) || {})?.assignments || [];
const expressionInFieldName: number[] = [];
for (const [index, assignment] of assignments.entries()) {
if (assignment.name.startsWith('=')) {
expressionInFieldName.push(index + 1);
}
}
if (expressionInFieldName.length > 0) {
nodeHints.push({
message: `An expression is used in 'Fields to Set' in ${expressionInFieldName.length === 1 ? 'field' : 'fields'} ${expressionInFieldName.join(', ')}, did you mean to use it in the value instead?`,
whenToDisplay: 'beforeExecution',
location: 'outputPane',
});
}
}
// Split In Batches setup hints
if (node.type === SPLIT_IN_BATCHES_NODE_TYPE) {
const { connectionsBySourceNode } = workflow;
const firstNodesInLoop = connectionsBySourceNode[node.name]?.main[1] || [];
if (!firstNodesInLoop.length) {
nodeHints.push({
message: "No nodes connected to the 'loop' output of this node",
whenToDisplay: 'beforeExecution',
location: 'outputPane',
});
} else {
for (const nodeInConnection of firstNodesInLoop || []) {
const nodeChilds = workflow.getChildNodes(nodeInConnection.node) || [];
if (!nodeChilds.includes(node.name)) {
nodeHints.push({
message:
"The last node in the branch of the 'loop' output must be connected back to the input of this node to loop correctly",
whenToDisplay: 'beforeExecution',
location: 'outputPane',
});
}
}
}
}
return nodeHints;
}