feat(core): Node hints improvements (no-changelog) (#9387)

Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
Michael Kret 2024-05-22 13:35:29 +03:00 committed by GitHub
parent 3761537880
commit 870412f093
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 113 additions and 28 deletions

View file

@ -225,8 +225,8 @@
// Callout
--color-callout-info-border: var(--color-foreground-base);
--color-callout-info-background: var(--color-foreground-xlight);
--color-callout-info-font: var(--color-info);
--color-callout-info-icon: var(--color-info);
--color-callout-info-font: var(--color-text-base);
--color-callout-info-icon: var(--color-text-light);
--color-callout-success-border: var(--color-success-light-2);
--color-callout-success-background: var(--color-success-tint-2);
--color-callout-success-font: var(--color-success);

View file

@ -1021,6 +1021,24 @@ export default defineComponent({
showIoSearchNoMatchContent(): boolean {
return this.hasNodeRun && !this.inputData.length && !!this.search;
},
parentNodeOutputData(): INodeExecutionData[] {
const workflow = this.workflowsStore.getCurrentWorkflow();
const parentNode = workflow.getParentNodesByDepth(this.node.name)[0];
let parentNodeData: INodeExecutionData[] = [];
if (parentNode?.name) {
parentNodeData = this.nodeHelpers.getNodeInputData(
workflow.getNode(parentNode?.name),
this.runIndex,
this.outputIndex,
'input',
this.connectionType,
);
}
return parentNodeData;
},
},
watch: {
node(newNode: INodeUi, prevNode: INodeUi) {
@ -1118,19 +1136,18 @@ export default defineComponent({
},
shouldHintBeDisplayed(hint: NodeHint): boolean {
const { location, whenToDisplay } = hint;
if (location) {
if (location === 'ndv') {
return true;
if (location === 'ndv' && !['input', 'output'].includes(this.paneType)) {
return false;
}
if (location === 'inputPane' && this.paneType === 'input') {
return true;
if (location === 'inputPane' && this.paneType !== 'input') {
return false;
}
if (location === 'outputPane' && this.paneType === 'output') {
return true;
if (location === 'outputPane' && this.paneType !== 'output') {
return false;
}
return false;
}
if (whenToDisplay === 'afterExecution' && !this.hasNodeRun) {
@ -1150,7 +1167,12 @@ export default defineComponent({
if (workflowNode) {
const executionHints = this.executionHints;
const nodeHints = NodeHelpers.getNodeHints(workflow, workflowNode, this.nodeType);
const nodeHints = NodeHelpers.getNodeHints(workflow, workflowNode, this.nodeType, {
runExecutionData: this.workflowExecution?.data ?? null,
runIndex: this.runIndex,
connectionInputData: this.parentNodeOutputData,
});
return executionHints.concat(nodeHints).filter(this.shouldHintBeDisplayed);
}
}

View file

@ -1,5 +1,5 @@
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import { NodeExecutionOutput, NodeOperationError } from 'n8n-workflow';
import { configurePostgres } from '../transport';
import { configureQueryRunner } from '../helpers/utils';
@ -17,7 +17,8 @@ export async function router(this: IExecuteFunctions): Promise<INodeExecutionDat
const credentials = (await this.getCredentials('postgres')) as PostgresNodeCredentials;
const options = this.getNodeParameter('options', 0, {}) as PostgresNodeOptions;
options.nodeVersion = this.getNode().typeVersion;
const node = this.getNode();
options.nodeVersion = node.typeVersion;
options.operation = operation;
const { db, pgp, sshClient } = await configurePostgres(credentials, options);
@ -62,5 +63,17 @@ export async function router(this: IExecuteFunctions): Promise<INodeExecutionDat
if (!db.$pool.ending) await db.$pool.end();
}
if (operation === 'select' && items.length > 1 && !node.executeOnce) {
return new NodeExecutionOutput(
[returnData],
[
{
message: `This node ran ${items.length} times, once for each input item. To run for the first item only, enable 'execute once' in the node settings`,
location: 'outputPane',
},
],
);
}
return [returnData];
}

View file

@ -1,14 +1,14 @@
import get from 'lodash/get';
import unset from 'lodash/unset';
import {
type IBinaryData,
NodeOperationError,
deepCopy,
type IDataObject,
type IExecuteFunctions,
type INodeExecutionData,
type INodeType,
type INodeTypeDescription,
import { NodeOperationError, deepCopy, NodeExecutionOutput } from 'n8n-workflow';
import type {
IBinaryData,
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
NodeExecutionHint,
} from 'n8n-workflow';
import { prepareFieldsArray } from '../utils/utils';
@ -111,6 +111,7 @@ export class SplitOut implements INodeType {
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const returnData: INodeExecutionData[] = [];
const items = this.getInputData();
const notFoundedFields: { [key: string]: boolean[] } = {};
for (let i = 0; i < items.length; i++) {
const fieldsToSplitOut = (this.getNodeParameter('fieldToSplitOut', i) as string)
@ -160,6 +161,14 @@ export class SplitOut implements INodeType {
if (entityToSplit === undefined) {
entityToSplit = [];
if (!notFoundedFields[fieldToSplitOut]) {
notFoundedFields[fieldToSplitOut] = [];
}
notFoundedFields[fieldToSplitOut].push(false);
} else {
if (notFoundedFields[fieldToSplitOut]) {
notFoundedFields[fieldToSplitOut].push(true);
}
}
if (typeof entityToSplit !== 'object' || entityToSplit === null) {
@ -254,6 +263,21 @@ export class SplitOut implements INodeType {
}
}
if (Object.keys(notFoundedFields).length) {
const hints: NodeExecutionHint[] = [];
for (const [field, values] of Object.entries(notFoundedFields)) {
if (values.every((value) => !value)) {
hints.push({
message: `The field '${field}' wasn't found in any input item`,
location: 'outputPane',
});
}
}
if (hints.length) return new NodeExecutionOutput([returnData], hints);
}
return [returnData];
}
}

View file

@ -43,6 +43,7 @@ import type {
GenericValue,
DisplayCondition,
NodeHint,
INodeExecutionData,
} from './Interfaces';
import {
isFilterValue,
@ -1125,6 +1126,11 @@ export function getNodeHints(
workflow: Workflow,
node: INode,
nodeTypeData: INodeTypeDescription,
nodeInputData?: {
runExecutionData: IRunExecutionData | null;
runIndex: number;
connectionInputData: INodeExecutionData[];
},
): NodeHint[] {
const hints: NodeHint[] = [];
@ -1132,12 +1138,32 @@ export function getNodeHints(
for (const hint of nodeTypeData.hints) {
if (hint.displayCondition) {
try {
const display = (workflow.expression.getSimpleParameterValue(
node,
hint.displayCondition,
'internal',
{},
) || false) as boolean;
let display;
if (nodeInputData === undefined) {
display = (workflow.expression.getSimpleParameterValue(
node,
hint.displayCondition,
'internal',
{},
) || false) as boolean;
} else {
const { runExecutionData, runIndex, connectionInputData } = nodeInputData;
display = workflow.expression.getParameterValue(
hint.displayCondition,
runExecutionData ?? null,
runIndex,
0,
node.name,
connectionInputData,
'manual',
{},
);
}
if (typeof display === 'string' && display.trim() === 'true') {
display = true;
}
if (typeof display !== 'boolean') {
console.warn(