feat(core): Add optional Error-Output (#7460)
Add an additional optional error output to which all items get sent that could not be processed.  Github issue / Community forum post (link here to close automatically): https://community.n8n.io/t/error-connector-for-nodes/3094 https://community.n8n.io/t/error-handling-at-node-level-detect-node-execution-status/26791 --------- Co-authored-by: OlegIvaniv <me@olegivaniv.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import * as NodeViewUtils from '@/utils/nodeViewUtils';
|
||||
import { useHistoryStore } from '@/stores/history.store';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import type { EndpointSpec } from '@jsplumb/common';
|
||||
import { getStyleTokenValue } from '@/utils';
|
||||
|
||||
const createAddInputEndpointSpec = (
|
||||
connectionName: NodeConnectionType,
|
||||
@@ -339,13 +340,13 @@ export const nodeBase = defineComponent({
|
||||
} = {};
|
||||
|
||||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const outputs = NodeHelpers.getNodeOutputs(workflow, this.data, nodeTypeData) || [];
|
||||
this.outputs = outputs;
|
||||
this.outputs = NodeHelpers.getNodeOutputs(workflow, this.data, nodeTypeData) || [];
|
||||
|
||||
// TODO: There are still a lot of references of "main" in NodesView and
|
||||
// other locations. So assume there will be more problems
|
||||
|
||||
outputs.forEach((value, i) => {
|
||||
let maxLabelLength = 0;
|
||||
const outputConfigurations: INodeOutputConfiguration[] = [];
|
||||
this.outputs.forEach((value, i) => {
|
||||
let outputConfiguration: INodeOutputConfiguration;
|
||||
if (typeof value === 'string') {
|
||||
outputConfiguration = {
|
||||
@@ -354,6 +355,24 @@ export const nodeBase = defineComponent({
|
||||
} else {
|
||||
outputConfiguration = value;
|
||||
}
|
||||
if (nodeTypeData.outputNames?.[i]) {
|
||||
outputConfiguration.displayName = nodeTypeData.outputNames[i];
|
||||
}
|
||||
|
||||
if (outputConfiguration.displayName) {
|
||||
maxLabelLength =
|
||||
outputConfiguration.displayName.length > maxLabelLength
|
||||
? outputConfiguration.displayName.length
|
||||
: maxLabelLength;
|
||||
}
|
||||
|
||||
outputConfigurations.push(outputConfiguration);
|
||||
});
|
||||
|
||||
const endpointLabelLength = maxLabelLength < 4 ? 'short' : 'medium';
|
||||
|
||||
this.outputs.forEach((value, i) => {
|
||||
const outputConfiguration = outputConfigurations[i];
|
||||
|
||||
const outputName: ConnectionTypes = outputConfiguration.type;
|
||||
|
||||
@@ -376,7 +395,7 @@ export const nodeBase = defineComponent({
|
||||
const rootTypeIndex = rootTypeIndexData[rootCategoryOutputName];
|
||||
const typeIndex = typeIndexData[outputName];
|
||||
|
||||
const outputsOfSameRootType = outputs.filter((outputData) => {
|
||||
const outputsOfSameRootType = this.outputs.filter((outputData) => {
|
||||
const thisOutputName: string =
|
||||
typeof outputData === 'string' ? outputData : outputData.type;
|
||||
return outputName === NodeConnectionType.Main
|
||||
@@ -417,7 +436,7 @@ export const nodeBase = defineComponent({
|
||||
hoverClass: 'dot-output-endpoint-hover',
|
||||
connectionsDirected: true,
|
||||
dragAllowedWhenFull: false,
|
||||
...this.__getOutputConnectionStyle(outputName, nodeTypeData),
|
||||
...this.__getOutputConnectionStyle(outputName, outputConfiguration, nodeTypeData),
|
||||
};
|
||||
|
||||
const endpoint = this.instance.addEndpoint(
|
||||
@@ -426,11 +445,12 @@ export const nodeBase = defineComponent({
|
||||
);
|
||||
|
||||
this.__addEndpointTestingData(endpoint, 'output', typeIndex);
|
||||
if (outputConfiguration.displayName || nodeTypeData.outputNames?.[i]) {
|
||||
if (outputConfiguration.displayName) {
|
||||
// Apply output names if they got set
|
||||
const overlaySpec = NodeViewUtils.getOutputNameOverlay(
|
||||
outputConfiguration.displayName || nodeTypeData.outputNames[i],
|
||||
outputConfiguration.displayName,
|
||||
outputName,
|
||||
outputConfiguration?.category,
|
||||
);
|
||||
endpoint.addOverlay(overlaySpec);
|
||||
}
|
||||
@@ -441,7 +461,7 @@ export const nodeBase = defineComponent({
|
||||
nodeId: this.nodeId,
|
||||
index: typeIndex,
|
||||
totalEndpoints: outputsOfSameRootType.length,
|
||||
nodeType: node.type,
|
||||
endpointLabelLength,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -455,9 +475,9 @@ export const nodeBase = defineComponent({
|
||||
options: {
|
||||
dimensions: 24,
|
||||
connectedEndpoint: endpoint,
|
||||
showOutputLabel: outputs.length === 1,
|
||||
size: outputs.length >= 3 ? 'small' : 'medium',
|
||||
nodeType: node.type,
|
||||
showOutputLabel: this.outputs.length === 1,
|
||||
size: this.outputs.length >= 3 ? 'small' : 'medium',
|
||||
endpointLabelLength,
|
||||
hoverMessage: this.$locale.baseText('nodeBase.clickToAddNodeOrDragToConnect'),
|
||||
},
|
||||
},
|
||||
@@ -475,10 +495,16 @@ export const nodeBase = defineComponent({
|
||||
nodeId: this.nodeId,
|
||||
type: outputName,
|
||||
index: typeIndex,
|
||||
category: outputConfiguration?.category,
|
||||
},
|
||||
cssClass: 'plus-draggable-endpoint',
|
||||
dragAllowedWhenFull: false,
|
||||
};
|
||||
|
||||
if (outputConfiguration?.category) {
|
||||
plusEndpointData.cssClass = `${plusEndpointData.cssClass} ${outputConfiguration?.category}`;
|
||||
}
|
||||
|
||||
const plusEndpoint = this.instance.addEndpoint(
|
||||
this.$refs[this.data.name] as Element,
|
||||
plusEndpointData,
|
||||
@@ -538,6 +564,7 @@ export const nodeBase = defineComponent({
|
||||
},
|
||||
__getOutputConnectionStyle(
|
||||
connectionType: ConnectionTypes,
|
||||
outputConfiguration: INodeOutputConfiguration,
|
||||
nodeTypeData: INodeTypeDescription,
|
||||
): EndpointOptions {
|
||||
const type = 'output';
|
||||
@@ -557,6 +584,18 @@ export const nodeBase = defineComponent({
|
||||
});
|
||||
|
||||
if (connectionType === NodeConnectionType.Main) {
|
||||
if (outputConfiguration.category === 'error') {
|
||||
return {
|
||||
paintStyle: {
|
||||
...NodeViewUtils.getOutputEndpointStyle(
|
||||
nodeTypeData,
|
||||
this.__getEndpointColor(NodeConnectionType.Main),
|
||||
),
|
||||
fill: getStyleTokenValue('--node-error-output-color', true),
|
||||
},
|
||||
cssClass: `dot-${type}-endpoint`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
paintStyle: NodeViewUtils.getOutputEndpointStyle(
|
||||
nodeTypeData,
|
||||
|
||||
@@ -649,6 +649,7 @@ export const workflowHelpers = defineComponent({
|
||||
'credentials',
|
||||
'disabled',
|
||||
'issues',
|
||||
'onError',
|
||||
'notes',
|
||||
'parameters',
|
||||
'status',
|
||||
@@ -725,14 +726,16 @@ export const workflowHelpers = defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
// Save the disabled property and continueOnFail only when is set
|
||||
// Save the disabled property, continueOnFail and onError only when is set
|
||||
if (node.disabled === true) {
|
||||
nodeData.disabled = true;
|
||||
}
|
||||
if (node.continueOnFail === true) {
|
||||
nodeData.continueOnFail = true;
|
||||
}
|
||||
|
||||
if (node.onError !== 'stopWorkflow') {
|
||||
nodeData.onError = node.onError;
|
||||
}
|
||||
// Save the notes only if when they contain data
|
||||
if (![undefined, ''].includes(node.notes)) {
|
||||
nodeData.notes = node.notes;
|
||||
|
||||
Reference in New Issue
Block a user