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
|
// Callout
|
||||||
--color-callout-info-border: var(--color-foreground-base);
|
--color-callout-info-border: var(--color-foreground-base);
|
||||||
--color-callout-info-background: var(--color-foreground-xlight);
|
--color-callout-info-background: var(--color-foreground-xlight);
|
||||||
--color-callout-info-font: var(--color-info);
|
--color-callout-info-font: var(--color-text-base);
|
||||||
--color-callout-info-icon: var(--color-info);
|
--color-callout-info-icon: var(--color-text-light);
|
||||||
--color-callout-success-border: var(--color-success-light-2);
|
--color-callout-success-border: var(--color-success-light-2);
|
||||||
--color-callout-success-background: var(--color-success-tint-2);
|
--color-callout-success-background: var(--color-success-tint-2);
|
||||||
--color-callout-success-font: var(--color-success);
|
--color-callout-success-font: var(--color-success);
|
||||||
|
|
|
@ -1021,6 +1021,24 @@ export default defineComponent({
|
||||||
showIoSearchNoMatchContent(): boolean {
|
showIoSearchNoMatchContent(): boolean {
|
||||||
return this.hasNodeRun && !this.inputData.length && !!this.search;
|
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: {
|
watch: {
|
||||||
node(newNode: INodeUi, prevNode: INodeUi) {
|
node(newNode: INodeUi, prevNode: INodeUi) {
|
||||||
|
@ -1118,19 +1136,18 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
shouldHintBeDisplayed(hint: NodeHint): boolean {
|
shouldHintBeDisplayed(hint: NodeHint): boolean {
|
||||||
const { location, whenToDisplay } = hint;
|
const { location, whenToDisplay } = hint;
|
||||||
|
|
||||||
if (location) {
|
if (location) {
|
||||||
if (location === 'ndv') {
|
if (location === 'ndv' && !['input', 'output'].includes(this.paneType)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (location === 'inputPane' && this.paneType === 'input') {
|
if (location === 'inputPane' && this.paneType !== 'input') {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location === 'outputPane' && this.paneType === 'output') {
|
if (location === 'outputPane' && this.paneType !== 'output') {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (whenToDisplay === 'afterExecution' && !this.hasNodeRun) {
|
if (whenToDisplay === 'afterExecution' && !this.hasNodeRun) {
|
||||||
|
@ -1150,7 +1167,12 @@ export default defineComponent({
|
||||||
|
|
||||||
if (workflowNode) {
|
if (workflowNode) {
|
||||||
const executionHints = this.executionHints;
|
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);
|
return executionHints.concat(nodeHints).filter(this.shouldHintBeDisplayed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||||
import { NodeOperationError } from 'n8n-workflow';
|
import { NodeExecutionOutput, NodeOperationError } from 'n8n-workflow';
|
||||||
|
|
||||||
import { configurePostgres } from '../transport';
|
import { configurePostgres } from '../transport';
|
||||||
import { configureQueryRunner } from '../helpers/utils';
|
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 credentials = (await this.getCredentials('postgres')) as PostgresNodeCredentials;
|
||||||
const options = this.getNodeParameter('options', 0, {}) as PostgresNodeOptions;
|
const options = this.getNodeParameter('options', 0, {}) as PostgresNodeOptions;
|
||||||
options.nodeVersion = this.getNode().typeVersion;
|
const node = this.getNode();
|
||||||
|
options.nodeVersion = node.typeVersion;
|
||||||
options.operation = operation;
|
options.operation = operation;
|
||||||
|
|
||||||
const { db, pgp, sshClient } = await configurePostgres(credentials, options);
|
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 (!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];
|
return [returnData];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import unset from 'lodash/unset';
|
import unset from 'lodash/unset';
|
||||||
import {
|
import { NodeOperationError, deepCopy, NodeExecutionOutput } from 'n8n-workflow';
|
||||||
type IBinaryData,
|
import type {
|
||||||
NodeOperationError,
|
IBinaryData,
|
||||||
deepCopy,
|
IDataObject,
|
||||||
type IDataObject,
|
IExecuteFunctions,
|
||||||
type IExecuteFunctions,
|
INodeExecutionData,
|
||||||
type INodeExecutionData,
|
INodeType,
|
||||||
type INodeType,
|
INodeTypeDescription,
|
||||||
type INodeTypeDescription,
|
NodeExecutionHint,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { prepareFieldsArray } from '../utils/utils';
|
import { prepareFieldsArray } from '../utils/utils';
|
||||||
|
|
||||||
|
@ -111,6 +111,7 @@ export class SplitOut implements INodeType {
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const returnData: INodeExecutionData[] = [];
|
const returnData: INodeExecutionData[] = [];
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
|
const notFoundedFields: { [key: string]: boolean[] } = {};
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const fieldsToSplitOut = (this.getNodeParameter('fieldToSplitOut', i) as string)
|
const fieldsToSplitOut = (this.getNodeParameter('fieldToSplitOut', i) as string)
|
||||||
|
@ -160,6 +161,14 @@ export class SplitOut implements INodeType {
|
||||||
|
|
||||||
if (entityToSplit === undefined) {
|
if (entityToSplit === undefined) {
|
||||||
entityToSplit = [];
|
entityToSplit = [];
|
||||||
|
if (!notFoundedFields[fieldToSplitOut]) {
|
||||||
|
notFoundedFields[fieldToSplitOut] = [];
|
||||||
|
}
|
||||||
|
notFoundedFields[fieldToSplitOut].push(false);
|
||||||
|
} else {
|
||||||
|
if (notFoundedFields[fieldToSplitOut]) {
|
||||||
|
notFoundedFields[fieldToSplitOut].push(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof entityToSplit !== 'object' || entityToSplit === null) {
|
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];
|
return [returnData];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import type {
|
||||||
GenericValue,
|
GenericValue,
|
||||||
DisplayCondition,
|
DisplayCondition,
|
||||||
NodeHint,
|
NodeHint,
|
||||||
|
INodeExecutionData,
|
||||||
} from './Interfaces';
|
} from './Interfaces';
|
||||||
import {
|
import {
|
||||||
isFilterValue,
|
isFilterValue,
|
||||||
|
@ -1125,6 +1126,11 @@ export function getNodeHints(
|
||||||
workflow: Workflow,
|
workflow: Workflow,
|
||||||
node: INode,
|
node: INode,
|
||||||
nodeTypeData: INodeTypeDescription,
|
nodeTypeData: INodeTypeDescription,
|
||||||
|
nodeInputData?: {
|
||||||
|
runExecutionData: IRunExecutionData | null;
|
||||||
|
runIndex: number;
|
||||||
|
connectionInputData: INodeExecutionData[];
|
||||||
|
},
|
||||||
): NodeHint[] {
|
): NodeHint[] {
|
||||||
const hints: NodeHint[] = [];
|
const hints: NodeHint[] = [];
|
||||||
|
|
||||||
|
@ -1132,12 +1138,32 @@ export function getNodeHints(
|
||||||
for (const hint of nodeTypeData.hints) {
|
for (const hint of nodeTypeData.hints) {
|
||||||
if (hint.displayCondition) {
|
if (hint.displayCondition) {
|
||||||
try {
|
try {
|
||||||
const display = (workflow.expression.getSimpleParameterValue(
|
let display;
|
||||||
node,
|
|
||||||
hint.displayCondition,
|
if (nodeInputData === undefined) {
|
||||||
'internal',
|
display = (workflow.expression.getSimpleParameterValue(
|
||||||
{},
|
node,
|
||||||
) || false) as boolean;
|
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') {
|
if (typeof display !== 'boolean') {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|
Loading…
Reference in a new issue