mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Separate node execution and validation error states (#19029)
This commit is contained in:
|
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 468 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.0506 2.38452C10.9161 0.882058 13.0845 0.882058 13.95 2.38452L23.3065 18.6267C24.1706 20.1267 23.0883 21.9997 21.3572 21.9998H2.6424C0.911559 21.9994 -0.170877 20.1266 0.693176 18.6267L10.0506 2.38452ZM11.9998 15.9998C11.1715 15.9999 10.4999 16.6715 10.4998 17.4998C10.4998 18.3281 11.1715 18.9997 11.9998 18.9998C12.8282 18.9998 13.4998 18.3282 13.4998 17.4998C13.4997 16.6714 12.8282 15.9998 11.9998 15.9998ZM11.9998 7.49976C11.1715 7.49986 10.4999 8.17148 10.4998 8.99976V12.4998C10.4998 13.3281 11.1715 13.9997 11.9998 13.9998C12.8282 13.9998 13.4998 13.3282 13.4998 12.4998V8.99976C13.4997 8.17142 12.8282 7.49976 11.9998 7.49976Z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 774 B |
@@ -5,12 +5,13 @@ import EmptyOutput from './custom/empty-output.svg';
|
|||||||
import GripLinesVertical from './custom/grip-lines-vertical.svg';
|
import GripLinesVertical from './custom/grip-lines-vertical.svg';
|
||||||
import NodeDirty from './custom/node-dirty.svg';
|
import NodeDirty from './custom/node-dirty.svg';
|
||||||
import NodeEllipsis from './custom/node-ellipsis.svg';
|
import NodeEllipsis from './custom/node-ellipsis.svg';
|
||||||
import NodeError from './custom/node-error.svg';
|
import NodeExecutionError from './custom/node-execution-error.svg';
|
||||||
import NodePin from './custom/node-pin.svg';
|
import NodePin from './custom/node-pin.svg';
|
||||||
import NodePlay from './custom/node-play.svg';
|
import NodePlay from './custom/node-play.svg';
|
||||||
import NodePower from './custom/node-power.svg';
|
import NodePower from './custom/node-power.svg';
|
||||||
import NodeSuccess from './custom/node-success.svg';
|
import NodeSuccess from './custom/node-success.svg';
|
||||||
import NodeTrash from './custom/node-trash.svg';
|
import NodeTrash from './custom/node-trash.svg';
|
||||||
|
import NodeValidationError from './custom/node-validation-error.svg';
|
||||||
import PopOut from './custom/pop-out.svg';
|
import PopOut from './custom/pop-out.svg';
|
||||||
import Retry from './custom/retry.svg';
|
import Retry from './custom/retry.svg';
|
||||||
import RunOnce from './custom/run-once.svg';
|
import RunOnce from './custom/run-once.svg';
|
||||||
@@ -434,7 +435,8 @@ export const updatedIconSet = {
|
|||||||
spinner: Spinner,
|
spinner: Spinner,
|
||||||
'node-dirty': NodeDirty,
|
'node-dirty': NodeDirty,
|
||||||
'node-ellipsis': NodeEllipsis,
|
'node-ellipsis': NodeEllipsis,
|
||||||
'node-error': NodeError,
|
'node-execution-error': NodeExecutionError,
|
||||||
|
'node-validation-error': NodeValidationError,
|
||||||
'node-pin': NodePin,
|
'node-pin': NodePin,
|
||||||
'node-play': NodePlay,
|
'node-play': NodePlay,
|
||||||
'node-power': NodePower,
|
'node-power': NodePower,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function createCanvasNodeData({
|
|||||||
outputs = [],
|
outputs = [],
|
||||||
connections = { [CanvasConnectionMode.Input]: {}, [CanvasConnectionMode.Output]: {} },
|
connections = { [CanvasConnectionMode.Input]: {}, [CanvasConnectionMode.Output]: {} },
|
||||||
execution = { running: false },
|
execution = { running: false },
|
||||||
issues = { items: [], visible: false },
|
issues = { execution: [], validation: [], visible: false },
|
||||||
pinnedData = { count: 0, visible: false },
|
pinnedData = { count: 0, visible: false },
|
||||||
runData = { outputMap: {}, iterations: 0, visible: false },
|
runData = { outputMap: {}, iterations: 0, visible: false },
|
||||||
render = {
|
render = {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const {
|
|||||||
executionWaitingForNext,
|
executionWaitingForNext,
|
||||||
executionRunning,
|
executionRunning,
|
||||||
hasRunData,
|
hasRunData,
|
||||||
hasIssues,
|
hasExecutionErrors,
|
||||||
render,
|
render,
|
||||||
} = useCanvasNode();
|
} = useCanvasNode();
|
||||||
const { mainOutputs, mainOutputConnections, mainInputs, mainInputConnections, nonMainInputs } =
|
const { mainOutputs, mainOutputConnections, mainInputs, mainInputConnections, nonMainInputs } =
|
||||||
@@ -54,7 +54,7 @@ const classes = computed(() => {
|
|||||||
[$style.selected]: isSelected.value,
|
[$style.selected]: isSelected.value,
|
||||||
[$style.disabled]: isDisabled.value,
|
[$style.disabled]: isDisabled.value,
|
||||||
[$style.success]: hasRunData.value,
|
[$style.success]: hasRunData.value,
|
||||||
[$style.error]: hasIssues.value,
|
[$style.error]: hasExecutionErrors.value,
|
||||||
[$style.pinned]: hasPinnedData.value,
|
[$style.pinned]: hasPinnedData.value,
|
||||||
[$style.waiting]: executionWaiting.value ?? executionStatus.value === 'waiting',
|
[$style.waiting]: executionWaiting.value ?? executionStatus.value === 'waiting',
|
||||||
[$style.running]: executionRunning.value || executionWaitingForNext.value,
|
[$style.running]: executionRunning.value || executionWaitingForNext.value,
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ const $style = useCssModule();
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
hasPinnedData,
|
hasPinnedData,
|
||||||
issues,
|
executionErrors,
|
||||||
hasIssues,
|
validationErrors,
|
||||||
|
hasExecutionErrors,
|
||||||
|
hasValidationErrors,
|
||||||
executionStatus,
|
executionStatus,
|
||||||
executionWaiting,
|
executionWaiting,
|
||||||
executionWaitingForNext,
|
executionWaitingForNext,
|
||||||
@@ -66,12 +68,6 @@ const commonClasses = computed(() => [
|
|||||||
<N8nIcon icon="clock" :size="size" />
|
<N8nIcon icon="clock" :size="size" />
|
||||||
</N8nTooltip>
|
</N8nTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="spinnerLayout === 'absolute'"
|
|
||||||
:class="[...commonClasses, $style['node-waiting-spinner']]"
|
|
||||||
>
|
|
||||||
<N8nIcon icon="refresh-cw" spin />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="isNodeExecuting"
|
v-else-if="isNodeExecuting"
|
||||||
@@ -84,15 +80,27 @@ const commonClasses = computed(() => [
|
|||||||
<N8nIcon icon="power" :size="size" />
|
<N8nIcon icon="power" :size="size" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="hasIssues && !hideNodeIssues"
|
v-else-if="hasExecutionErrors && !hideNodeIssues"
|
||||||
:class="[...commonClasses, $style.issues]"
|
:class="[...commonClasses, $style.issues]"
|
||||||
data-test-id="node-issues"
|
data-test-id="node-issues"
|
||||||
>
|
>
|
||||||
<N8nTooltip :show-after="500" placement="bottom">
|
<N8nTooltip :show-after="500" placement="bottom">
|
||||||
<template #content>
|
<template #content>
|
||||||
<TitledList :title="`${i18n.baseText('node.issues')}:`" :items="issues" />
|
<TitledList :title="`${i18n.baseText('node.issues')}:`" :items="executionErrors" />
|
||||||
</template>
|
</template>
|
||||||
<N8nIcon icon="node-error" :size="size" />
|
<N8nIcon icon="node-execution-error" :size="size" />
|
||||||
|
</N8nTooltip>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="hasValidationErrors && !hideNodeIssues"
|
||||||
|
:class="[...commonClasses, $style.issues]"
|
||||||
|
data-test-id="node-issues"
|
||||||
|
>
|
||||||
|
<N8nTooltip :show-after="500" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
<TitledList :title="`${i18n.baseText('node.issues')}:`" :items="validationErrors" />
|
||||||
|
</template>
|
||||||
|
<N8nIcon icon="node-validation-error" :size="size" />
|
||||||
</N8nTooltip>
|
</N8nTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="executionStatus === 'unknown'">
|
<div v-else-if="executionStatus === 'unknown'">
|
||||||
@@ -152,7 +160,6 @@ const commonClasses = computed(() => [
|
|||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-waiting-spinner,
|
|
||||||
.running {
|
.running {
|
||||||
color: hsl(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l));
|
color: hsl(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l));
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ describe('useCanvasMapping', () => {
|
|||||||
waitingForNext: false,
|
waitingForNext: false,
|
||||||
},
|
},
|
||||||
issues: {
|
issues: {
|
||||||
items: [],
|
execution: [],
|
||||||
|
validation: [],
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
pinnedData: {
|
pinnedData: {
|
||||||
@@ -705,8 +706,8 @@ describe('useCanvasMapping', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nodeIssuesById', () => {
|
describe('node issues', () => {
|
||||||
it('should return empty array when node has no issues', () => {
|
it('should return empty arrays when node has no issues', () => {
|
||||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||||
const node = createTestNode({ name: 'Test Node' });
|
const node = createTestNode({ name: 'Test Node' });
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
@@ -715,13 +716,17 @@ describe('useCanvasMapping', () => {
|
|||||||
|
|
||||||
workflowsStore.getWorkflowRunData = {};
|
workflowsStore.getWorkflowRunData = {};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([]);
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
|
execution: [],
|
||||||
|
validation: [],
|
||||||
|
visible: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle execution errors', () => {
|
it('should handle execution errors', () => {
|
||||||
@@ -748,13 +753,17 @@ describe('useCanvasMapping', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([`${errorMessage} (${errorDescription})`]);
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
|
execution: [`${errorMessage} (${errorDescription})`],
|
||||||
|
validation: [],
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle execution error without description', () => {
|
it('should handle execution error without description', () => {
|
||||||
@@ -780,13 +789,17 @@ describe('useCanvasMapping', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([errorMessage]);
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
|
execution: [errorMessage],
|
||||||
|
validation: [],
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle multiple execution errors', () => {
|
it('should handle multiple execution errors', () => {
|
||||||
@@ -821,16 +834,17 @@ describe('useCanvasMapping', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
'Error 1 (Description 1)',
|
execution: ['Error 1 (Description 1)', 'Error 2 (Description 2)'],
|
||||||
'Error 2 (Description 2)',
|
validation: [],
|
||||||
]);
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle node issues', () => {
|
it('should handle node issues', () => {
|
||||||
@@ -847,15 +861,17 @@ describe('useCanvasMapping', () => {
|
|||||||
|
|
||||||
workflowsStore.getWorkflowRunData = {};
|
workflowsStore.getWorkflowRunData = {};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
'Node Type "n8n-nodes-base.set" is not known.',
|
execution: [],
|
||||||
]);
|
validation: ['Node Type "n8n-nodes-base.set" is not known.'],
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should combine execution errors and node issues', () => {
|
it('should combine execution errors and node issues', () => {
|
||||||
@@ -885,16 +901,17 @@ describe('useCanvasMapping', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node.id]).toEqual([
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
'Execution error (Error description)',
|
execution: ['Execution error (Error description)'],
|
||||||
'Node Type "n8n-nodes-base.set" is not known.',
|
validation: ['Node Type "n8n-nodes-base.set" is not known.'],
|
||||||
]);
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle multiple nodes with different issues', () => {
|
it('should handle multiple nodes with different issues', () => {
|
||||||
@@ -925,16 +942,22 @@ describe('useCanvasMapping', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodeIssuesById } = useCanvasMapping({
|
const { nodes: mappedNodes } = useCanvasMapping({
|
||||||
nodes: ref(nodes),
|
nodes: ref(nodes),
|
||||||
connections: ref(connections),
|
connections: ref(connections),
|
||||||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(nodeIssuesById.value[node1.id]).toEqual([
|
expect(mappedNodes.value[0]?.data?.issues).toEqual({
|
||||||
'Node Type "n8n-nodes-base.set" is not known.',
|
execution: [],
|
||||||
]);
|
validation: ['Node Type "n8n-nodes-base.set" is not known.'],
|
||||||
expect(nodeIssuesById.value[node2.id]).toEqual(['Execution error (Error description)']);
|
visible: true,
|
||||||
|
});
|
||||||
|
expect(mappedNodes.value[1]?.data?.issues).toEqual({
|
||||||
|
execution: ['Execution error (Error description)'],
|
||||||
|
validation: [],
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -395,25 +395,35 @@ export function useCanvasMapping({
|
|||||||
{ throttle: CANVAS_EXECUTION_DATA_THROTTLE_DURATION, immediate: true },
|
{ throttle: CANVAS_EXECUTION_DATA_THROTTLE_DURATION, immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const nodeIssuesById = computed(() =>
|
const nodeExecutionErrorsById = computed(() =>
|
||||||
nodes.value.reduce<Record<string, string[]>>((acc, node) => {
|
nodes.value.reduce<Record<string, string[]>>((acc, node) => {
|
||||||
const issues: string[] = [];
|
const executionErrors: string[] = [];
|
||||||
const nodeExecutionRunData = workflowsStore.getWorkflowRunData?.[node.name];
|
const nodeExecutionRunData = workflowsStore.getWorkflowRunData?.[node.name];
|
||||||
if (nodeExecutionRunData) {
|
if (nodeExecutionRunData) {
|
||||||
nodeExecutionRunData.forEach((executionRunData) => {
|
nodeExecutionRunData.forEach((executionRunData) => {
|
||||||
if (executionRunData?.error) {
|
if (executionRunData?.error) {
|
||||||
const { message, description } = executionRunData.error;
|
const { message, description } = executionRunData.error;
|
||||||
const issue = `${message}${description ? ` (${description})` : ''}`;
|
const issue = `${message}${description ? ` (${description})` : ''}`;
|
||||||
issues.push(sanitizeHtml(issue));
|
executionErrors.push(sanitizeHtml(issue));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acc[node.id] = executionErrors;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const nodeValidationErrorsById = computed(() =>
|
||||||
|
nodes.value.reduce<Record<string, string[]>>((acc, node) => {
|
||||||
|
const validationErrors: string[] = [];
|
||||||
|
|
||||||
if (node?.issues !== undefined) {
|
if (node?.issues !== undefined) {
|
||||||
issues.push(...nodeHelpers.nodeIssuesToString(node.issues, node));
|
validationErrors.push(...nodeHelpers.nodeIssuesToString(node.issues, node));
|
||||||
}
|
}
|
||||||
|
|
||||||
acc[node.id] = issues;
|
acc[node.id] = validationErrors;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
@@ -421,15 +431,19 @@ export function useCanvasMapping({
|
|||||||
|
|
||||||
const nodeHasIssuesById = computed(() =>
|
const nodeHasIssuesById = computed(() =>
|
||||||
nodes.value.reduce<Record<string, boolean>>((acc, node) => {
|
nodes.value.reduce<Record<string, boolean>>((acc, node) => {
|
||||||
|
const hasExecutionErrors = nodeExecutionErrorsById.value[node.id]?.length > 0;
|
||||||
|
const hasValidationErrors = nodeValidationErrorsById.value[node.id]?.length > 0;
|
||||||
|
|
||||||
if (['crashed', 'error'].includes(nodeExecutionStatusById.value[node.id])) {
|
if (['crashed', 'error'].includes(nodeExecutionStatusById.value[node.id])) {
|
||||||
acc[node.id] = true;
|
acc[node.id] = true;
|
||||||
} else if (nodePinnedDataById.value[node.id]) {
|
} else if (nodePinnedDataById.value[node.id]) {
|
||||||
acc[node.id] = false;
|
acc[node.id] = false;
|
||||||
} else if (node.issues && nodeHelpers.nodeIssuesToString(node.issues, node).length) {
|
} else if (hasValidationErrors) {
|
||||||
|
acc[node.id] = true;
|
||||||
|
} else if (hasExecutionErrors) {
|
||||||
acc[node.id] = true;
|
acc[node.id] = true;
|
||||||
} else {
|
} else {
|
||||||
const tasks = workflowsStore.getWorkflowRunData?.[node.name] ?? [];
|
const tasks = workflowsStore.getWorkflowRunData?.[node.name] ?? [];
|
||||||
|
|
||||||
acc[node.id] = Boolean(tasks.at(-1)?.error);
|
acc[node.id] = Boolean(tasks.at(-1)?.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +619,8 @@ export function useCanvasMapping({
|
|||||||
[CanvasConnectionMode.Output]: outputConnections,
|
[CanvasConnectionMode.Output]: outputConnections,
|
||||||
},
|
},
|
||||||
issues: {
|
issues: {
|
||||||
items: nodeIssuesById.value[node.id],
|
execution: nodeExecutionErrorsById.value[node.id],
|
||||||
|
validation: nodeValidationErrorsById.value[node.id],
|
||||||
visible: nodeHasIssuesById.value[node.id],
|
visible: nodeHasIssuesById.value[node.id],
|
||||||
},
|
},
|
||||||
pinnedData: {
|
pinnedData: {
|
||||||
@@ -734,7 +749,6 @@ export function useCanvasMapping({
|
|||||||
additionalNodePropertiesById,
|
additionalNodePropertiesById,
|
||||||
nodeExecutionRunDataOutputMapById,
|
nodeExecutionRunDataOutputMapById,
|
||||||
nodeExecutionWaitingForNextById,
|
nodeExecutionWaitingForNextById,
|
||||||
nodeIssuesById,
|
|
||||||
nodeHasIssuesById,
|
nodeHasIssuesById,
|
||||||
connections: mappedConnections,
|
connections: mappedConnections,
|
||||||
nodes: mappedNodes,
|
nodes: mappedNodes,
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ describe('useCanvasNode', () => {
|
|||||||
[CanvasConnectionMode.Input]: { '0': [] },
|
[CanvasConnectionMode.Input]: { '0': [] },
|
||||||
[CanvasConnectionMode.Output]: {},
|
[CanvasConnectionMode.Output]: {},
|
||||||
},
|
},
|
||||||
issues: { items: ['issue1'], visible: true },
|
issues: {
|
||||||
|
execution: ['execution_error1'],
|
||||||
|
validation: ['validation_error1'],
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
execution: { status: 'running', waiting: 'waiting', running: true },
|
execution: { status: 'running', waiting: 'waiting', running: true },
|
||||||
runData: { outputMap: {}, iterations: 1, visible: true },
|
runData: { outputMap: {}, iterations: 1, visible: true },
|
||||||
pinnedData: { count: 1, visible: true },
|
pinnedData: { count: 1, visible: true },
|
||||||
@@ -90,7 +94,7 @@ describe('useCanvasNode', () => {
|
|||||||
expect(result.runDataOutputMap.value).toEqual({});
|
expect(result.runDataOutputMap.value).toEqual({});
|
||||||
expect(result.runDataIterations.value).toBe(1);
|
expect(result.runDataIterations.value).toBe(1);
|
||||||
expect(result.hasRunData.value).toBe(true);
|
expect(result.hasRunData.value).toBe(true);
|
||||||
expect(result.issues.value).toEqual(['issue1']);
|
expect(result.issues.value).toEqual(['execution_error1', 'validation_error1']);
|
||||||
expect(result.hasIssues.value).toBe(true);
|
expect(result.hasIssues.value).toBe(true);
|
||||||
expect(result.executionStatus.value).toBe('running');
|
expect(result.executionStatus.value).toBe('running');
|
||||||
expect(result.executionWaiting.value).toBe('waiting');
|
expect(result.executionWaiting.value).toBe('waiting');
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function useCanvasNode() {
|
|||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: [],
|
outputs: [],
|
||||||
connections: { [CanvasConnectionMode.Input]: {}, [CanvasConnectionMode.Output]: {} },
|
connections: { [CanvasConnectionMode.Input]: {}, [CanvasConnectionMode.Output]: {} },
|
||||||
issues: { items: [], visible: false },
|
issues: { execution: [], validation: [], visible: false },
|
||||||
pinnedData: { count: 0, visible: false },
|
pinnedData: { count: 0, visible: false },
|
||||||
execution: {
|
execution: {
|
||||||
running: false,
|
running: false,
|
||||||
@@ -47,8 +47,12 @@ export function useCanvasNode() {
|
|||||||
const pinnedDataCount = computed(() => data.value.pinnedData.count);
|
const pinnedDataCount = computed(() => data.value.pinnedData.count);
|
||||||
const hasPinnedData = computed(() => data.value.pinnedData.count > 0);
|
const hasPinnedData = computed(() => data.value.pinnedData.count > 0);
|
||||||
|
|
||||||
const issues = computed(() => data.value.issues.items ?? []);
|
const issues = computed(() => [...data.value.issues.execution, ...data.value.issues.validation]);
|
||||||
|
const executionErrors = computed(() => data.value.issues.execution ?? []);
|
||||||
|
const validationErrors = computed(() => data.value.issues.validation ?? []);
|
||||||
const hasIssues = computed(() => data.value.issues.visible);
|
const hasIssues = computed(() => data.value.issues.visible);
|
||||||
|
const hasExecutionErrors = computed(() => data.value.issues.execution.length > 0);
|
||||||
|
const hasValidationErrors = computed(() => data.value.issues.validation.length > 0);
|
||||||
|
|
||||||
const executionStatus = computed(() => data.value.execution.status);
|
const executionStatus = computed(() => data.value.execution.status);
|
||||||
const executionWaiting = computed(() => data.value.execution.waiting);
|
const executionWaiting = computed(() => data.value.execution.waiting);
|
||||||
@@ -81,7 +85,11 @@ export function useCanvasNode() {
|
|||||||
runDataOutputMap,
|
runDataOutputMap,
|
||||||
hasRunData,
|
hasRunData,
|
||||||
issues,
|
issues,
|
||||||
|
executionErrors,
|
||||||
|
validationErrors,
|
||||||
hasIssues,
|
hasIssues,
|
||||||
|
hasExecutionErrors,
|
||||||
|
hasValidationErrors,
|
||||||
executionStatus,
|
executionStatus,
|
||||||
executionWaiting,
|
executionWaiting,
|
||||||
executionWaitingForNext,
|
executionWaitingForNext,
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ vi.mock('@/composables/useCanvasMapping', () => ({
|
|||||||
additionalNodePropertiesById: computed(() => ({})),
|
additionalNodePropertiesById: computed(() => ({})),
|
||||||
nodeExecutionRunDataOutputMapById: computed(() => ({})),
|
nodeExecutionRunDataOutputMapById: computed(() => ({})),
|
||||||
nodeExecutionWaitingForNextById: computed(() => ({})),
|
nodeExecutionWaitingForNextById: computed(() => ({})),
|
||||||
nodeIssuesById: computed(() => ({})),
|
|
||||||
nodeHasIssuesById: computed(() => ({})),
|
nodeHasIssuesById: computed(() => ({})),
|
||||||
nodes: computed(() => []),
|
nodes: computed(() => []),
|
||||||
connections: computed(() => []),
|
connections: computed(() => []),
|
||||||
@@ -401,7 +400,6 @@ describe('useWorkflowDiff', () => {
|
|||||||
additionalNodePropertiesById: computed(() => ({}) as Record<string, Partial<CanvasNode>>),
|
additionalNodePropertiesById: computed(() => ({}) as Record<string, Partial<CanvasNode>>),
|
||||||
nodeExecutionRunDataOutputMapById: computed(() => ({}) as Record<string, ExecutionOutputMap>),
|
nodeExecutionRunDataOutputMapById: computed(() => ({}) as Record<string, ExecutionOutputMap>),
|
||||||
nodeExecutionWaitingForNextById: computed(() => ({}) as Record<string, boolean>),
|
nodeExecutionWaitingForNextById: computed(() => ({}) as Record<string, boolean>),
|
||||||
nodeIssuesById: computed(() => ({}) as Record<string, string[]>),
|
|
||||||
nodeHasIssuesById: computed(() => ({}) as Record<string, boolean>),
|
nodeHasIssuesById: computed(() => ({}) as Record<string, boolean>),
|
||||||
nodes: computed(() => nodes as CanvasNode[]),
|
nodes: computed(() => nodes as CanvasNode[]),
|
||||||
connections: computed(() => connections as CanvasConnection[]),
|
connections: computed(() => connections as CanvasConnection[]),
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ export interface CanvasNodeData {
|
|||||||
[CanvasConnectionMode.Output]: INodeConnections;
|
[CanvasConnectionMode.Output]: INodeConnections;
|
||||||
};
|
};
|
||||||
issues: {
|
issues: {
|
||||||
items: string[];
|
execution: string[];
|
||||||
|
validation: string[];
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
};
|
};
|
||||||
pinnedData: {
|
pinnedData: {
|
||||||
|
|||||||
Reference in New Issue
Block a user