feat(core): Node hints(warnings) system (#8954)

This commit is contained in:
Michael Kret
2024-05-13 15:46:02 +03:00
committed by GitHub
parent 4d2115c163
commit da6088d0bb
7 changed files with 265 additions and 6 deletions

View File

@@ -392,7 +392,7 @@ export interface IGetExecuteTriggerFunctions {
}
export interface IRunNodeResponse {
data: INodeExecutionData[][] | null | undefined;
data: INodeExecutionData[][] | NodeExecutionOutput | null | undefined;
closeFunction?: CloseFunction;
}
export interface IGetExecuteFunctions {
@@ -1423,6 +1423,20 @@ export interface SupplyData {
closeFunction?: CloseFunction;
}
export class NodeExecutionOutput extends Array {
private hints: NodeExecutionHint[];
constructor(data: INodeExecutionData[][], hints: NodeExecutionHint[] = []) {
super();
this.push(...data);
this.hints = hints;
}
public getHints(): NodeExecutionHint[] {
return this.hints;
}
}
export interface INodeType {
description: INodeTypeDescription;
supplyData?(this: IAllExecuteFunctions, itemIndex: number): Promise<SupplyData>;
@@ -1745,9 +1759,20 @@ export interface INodeTypeDescription extends INodeTypeBaseDescription {
}
| boolean;
extendsCredential?: string;
hints?: NodeHint[];
__loadOptionsMethods?: string[]; // only for validation during build
}
export type NodeHint = {
message: string;
type?: 'info' | 'warning' | 'danger';
location?: 'outputPane' | 'inputPane' | 'ndv';
displayCondition?: string;
whenToDisplay?: 'always' | 'beforeExecution' | 'afterExecution';
};
export type NodeExecutionHint = Omit<NodeHint, 'whenToDisplay' | 'displayCondition'>;
export interface INodeHookDescription {
method: string;
}
@@ -1929,6 +1954,7 @@ export interface ITaskData {
data?: ITaskDataConnections;
inputOverride?: ITaskDataConnections;
error?: ExecutionError;
hints?: NodeExecutionHint[];
source: Array<ISourceData | null>; // Is an array as nodes have multiple inputs
metadata?: ITaskMetadata;
}

View File

@@ -42,6 +42,7 @@ import type {
INodeInputConfiguration,
GenericValue,
DisplayCondition,
NodeHint,
} from './Interfaces';
import {
isFilterValue,
@@ -1120,6 +1121,50 @@ export function getNodeInputs(
}
}
export function getNodeHints(
workflow: Workflow,
node: INode,
nodeTypeData: INodeTypeDescription,
): NodeHint[] {
const hints: NodeHint[] = [];
if (nodeTypeData?.hints?.length) {
for (const hint of nodeTypeData.hints) {
if (hint.displayCondition) {
try {
const display = (workflow.expression.getSimpleParameterValue(
node,
hint.displayCondition,
'internal',
{},
) || false) as boolean;
if (typeof display !== 'boolean') {
console.warn(
`Condition was not resolved as boolean in '${node.name}' node for hint: `,
hint.message,
);
continue;
}
if (display) {
hints.push(hint);
}
} catch (e) {
console.warn(
`Could not calculate display condition in '${node.name}' node for hint: `,
hint.message,
);
}
} else {
hints.push(hint);
}
}
}
return hints;
}
export function getNodeOutputs(
workflow: Workflow,
node: INode,

View File

@@ -1,5 +1,7 @@
import type { INodeParameters, INodeProperties } from '@/Interfaces';
import { getNodeParameters } from '@/NodeHelpers';
import type { INode, INodeParameters, INodeProperties, INodeTypeDescription } from '@/Interfaces';
import type { Workflow } from '../src';
import { getNodeParameters, getNodeHints } from '@/NodeHelpers';
describe('NodeHelpers', () => {
describe('getNodeParameters', () => {
@@ -3437,4 +3439,93 @@ describe('NodeHelpers', () => {
});
}
});
describe('getNodeHints', () => {
//TODO: Add more tests here when hints are added to some node types
test('should return node hints if present in node type', () => {
const testType = {
hints: [
{
message: 'TEST HINT',
},
],
} as INodeTypeDescription;
const workflow = {} as unknown as Workflow;
const node: INode = {
name: 'Test Node Hints',
} as INode;
const nodeType = testType;
const hints = getNodeHints(workflow, node, nodeType);
expect(hints).toHaveLength(1);
expect(hints[0].message).toEqual('TEST HINT');
});
test('should not include hint if displayCondition is false', () => {
const testType = {
hints: [
{
message: 'TEST HINT',
displayCondition: 'FALSE DISPLAY CONDITION EXPESSION',
},
],
} as INodeTypeDescription;
const workflow = {
expression: {
getSimpleParameterValue(
_node: string,
_parameter: string,
_mode: string,
_additionalData = {},
) {
return false;
},
},
} as unknown as Workflow;
const node: INode = {
name: 'Test Node Hints',
} as INode;
const nodeType = testType;
const hints = getNodeHints(workflow, node, nodeType);
expect(hints).toHaveLength(0);
});
test('should include hint if displayCondition is true', () => {
const testType = {
hints: [
{
message: 'TEST HINT',
displayCondition: 'TRUE DISPLAY CONDITION EXPESSION',
},
],
} as INodeTypeDescription;
const workflow = {
expression: {
getSimpleParameterValue(
_node: string,
_parameter: string,
_mode: string,
_additionalData = {},
) {
return true;
},
},
} as unknown as Workflow;
const node: INode = {
name: 'Test Node Hints',
} as INode;
const nodeType = testType;
const hints = getNodeHints(workflow, node, nodeType);
expect(hints).toHaveLength(1);
});
});
});