mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(core): Node hints improvements (no-changelog) (#9387)
Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
parent
3761537880
commit
870412f093
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue