mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Cannot expand sub execution log if it finished with an error (#16236)
Co-authored-by: Csaba Tuncsik <csaba.tuncsik@gmail.com>
This commit is contained in:
@@ -31,7 +31,6 @@
|
||||
"@codemirror/view": "^6.26.3",
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@lezer/common": "1.1.0",
|
||||
"@n8n/rest-api-client": "workspace:*",
|
||||
"@n8n/api-types": "workspace:*",
|
||||
"@n8n/chat": "workspace:*",
|
||||
"@n8n/codemirror-lang": "workspace:*",
|
||||
@@ -41,6 +40,7 @@
|
||||
"@n8n/design-system": "workspace:*",
|
||||
"@n8n/i18n": "workspace:*",
|
||||
"@n8n/permissions": "workspace:*",
|
||||
"@n8n/rest-api-client": "workspace:*",
|
||||
"@n8n/stores": "workspace:*",
|
||||
"@n8n/utils": "workspace:*",
|
||||
"@replit/codemirror-indentation-markers": "^6.5.3",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useI18n } from '@n8n/i18n';
|
||||
import { I18nT } from 'vue-i18n';
|
||||
import { toDayMonth, toTime } from '@/utils/formatters/dateFormatter';
|
||||
import LogsViewNodeName from '@/features/logs/components/LogsViewNodeName.vue';
|
||||
import { getSubtreeTotalConsumedTokens } from '@/features/logs/logs.utils';
|
||||
import { getSubtreeTotalConsumedTokens, hasSubExecution } from '@/features/logs/logs.utils';
|
||||
import { useTimestamp } from '@vueuse/core';
|
||||
import type { LatestNodeInfo, LogEntry } from '@/features/logs/logs.types';
|
||||
|
||||
@@ -70,9 +70,7 @@ const subtreeConsumedTokens = computed(() =>
|
||||
props.shouldShowTokenCountColumn ? getSubtreeTotalConsumedTokens(props.data, false) : undefined,
|
||||
);
|
||||
|
||||
const hasChildren = computed(
|
||||
() => props.data.children.length > 0 || !!props.data.runData?.metadata?.subExecution,
|
||||
);
|
||||
const hasChildren = computed(() => props.data.children.length > 0 || hasSubExecution(props.data));
|
||||
|
||||
function isLastChild(level: number) {
|
||||
let parent = props.data.parent;
|
||||
|
||||
@@ -4,7 +4,12 @@ import { Workflow, type IRunExecutionData } from 'n8n-workflow';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { useThrottleFn } from '@vueuse/core';
|
||||
import { createLogTree, deepToRaw, mergeStartData } from '@/features/logs/logs.utils';
|
||||
import {
|
||||
createLogTree,
|
||||
deepToRaw,
|
||||
findSubExecutionLocator,
|
||||
mergeStartData,
|
||||
} from '@/features/logs/logs.utils';
|
||||
import { parse } from 'flatted';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import type { LatestNodeInfo, LogEntry } from '../logs.types';
|
||||
@@ -68,15 +73,14 @@ export function useLogsExecutionData() {
|
||||
}
|
||||
|
||||
async function loadSubExecution(logEntry: LogEntry) {
|
||||
const executionId = logEntry.runData?.metadata?.subExecution?.executionId;
|
||||
const workflowId = logEntry.runData?.metadata?.subExecution?.workflowId;
|
||||
const locator = findSubExecutionLocator(logEntry);
|
||||
|
||||
if (!execData.value?.data || !executionId || !workflowId) {
|
||||
if (!execData.value?.data || locator === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const subExecution = await workflowsStore.fetchExecutionDataById(executionId);
|
||||
const subExecution = await workflowsStore.fetchExecutionDataById(locator.executionId);
|
||||
const data = subExecution?.data
|
||||
? (parse(subExecution.data as unknown as string) as IRunExecutionData)
|
||||
: undefined;
|
||||
@@ -85,8 +89,8 @@ export function useLogsExecutionData() {
|
||||
throw Error('Data is missing');
|
||||
}
|
||||
|
||||
subWorkflowExecData.value[executionId] = data;
|
||||
subWorkflows.value[workflowId] = new Workflow({
|
||||
subWorkflowExecData.value[locator.executionId] = data;
|
||||
subWorkflows.value[locator.workflowId] = new Workflow({
|
||||
...subExecution.workflowData,
|
||||
nodeTypes: workflowsStore.getNodeTypes(),
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createLogTree,
|
||||
deepToRaw,
|
||||
findSelectedLogEntry,
|
||||
findSubExecutionLocator,
|
||||
getDefaultCollapsedEntries,
|
||||
getTreeNodeData,
|
||||
mergeStartData,
|
||||
@@ -1271,3 +1272,43 @@ describe(restoreChatHistory, () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(findSubExecutionLocator, () => {
|
||||
it('should return undefined if given log entry has no related sub execution', () => {
|
||||
const found = findSubExecutionLocator(
|
||||
createTestLogEntry({
|
||||
runData: createTestTaskData({
|
||||
metadata: {},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(found).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should find workflowId and executionId in metadata', () => {
|
||||
const found = findSubExecutionLocator(
|
||||
createTestLogEntry({
|
||||
runData: createTestTaskData({
|
||||
metadata: { subExecution: { workflowId: 'w0', executionId: 'e0' } },
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(found).toEqual({ workflowId: 'w0', executionId: 'e0' });
|
||||
});
|
||||
|
||||
it('should find workflowId and executionId in error object', () => {
|
||||
const found = findSubExecutionLocator(
|
||||
createTestLogEntry({
|
||||
runData: createTestTaskData({
|
||||
error: {
|
||||
errorResponse: { workflowId: 'w1', executionId: 'e1' },
|
||||
} as unknown as ExecutionError,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(found).toEqual({ workflowId: 'w1', executionId: 'e1' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
type Workflow,
|
||||
type INode,
|
||||
type ISourceData,
|
||||
parseErrorMetadata,
|
||||
type RelatedExecution,
|
||||
} from 'n8n-workflow';
|
||||
import type { LogEntry, LogEntrySelection, LogTreeCreationContext } from './logs.types';
|
||||
import { isProxy, isReactive, isRef, toRaw } from 'vue';
|
||||
@@ -70,13 +72,13 @@ function getChildNodes(
|
||||
runIndex: number | undefined,
|
||||
context: LogTreeCreationContext,
|
||||
) {
|
||||
if (hasSubExecution(treeNode)) {
|
||||
const workflowId = treeNode.runData?.metadata?.subExecution?.workflowId;
|
||||
const executionId = treeNode.runData?.metadata?.subExecution?.executionId;
|
||||
const workflow = workflowId ? context.workflows[workflowId] : undefined;
|
||||
const subWorkflowRunData = executionId ? context.subWorkflowData[executionId] : undefined;
|
||||
const subExecutionLocator = findSubExecutionLocator(treeNode);
|
||||
|
||||
if (!workflow || !subWorkflowRunData || !executionId) {
|
||||
if (subExecutionLocator !== undefined) {
|
||||
const workflow = context.workflows[subExecutionLocator.workflowId];
|
||||
const subWorkflowRunData = context.subWorkflowData[subExecutionLocator.executionId];
|
||||
|
||||
if (!workflow || !subWorkflowRunData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ function getChildNodes(
|
||||
parent: treeNode,
|
||||
depth: context.depth + 1,
|
||||
workflow,
|
||||
executionId,
|
||||
executionId: subExecutionLocator.executionId,
|
||||
data: subWorkflowRunData,
|
||||
});
|
||||
}
|
||||
@@ -434,7 +436,17 @@ export function mergeStartData(
|
||||
}
|
||||
|
||||
export function hasSubExecution(entry: LogEntry): boolean {
|
||||
return !!entry.runData?.metadata?.subExecution;
|
||||
return findSubExecutionLocator(entry) !== undefined;
|
||||
}
|
||||
|
||||
export function findSubExecutionLocator(entry: LogEntry): RelatedExecution | undefined {
|
||||
const metadata = entry.runData?.metadata?.subExecution;
|
||||
|
||||
if (metadata) {
|
||||
return { workflowId: metadata.workflowId, executionId: metadata.executionId };
|
||||
}
|
||||
|
||||
return parseErrorMetadata(entry.runData?.error)?.subExecution;
|
||||
}
|
||||
|
||||
export function getDefaultCollapsedEntries(entries: LogEntry[]): Record<string, boolean> {
|
||||
|
||||
@@ -38,7 +38,7 @@ catalog:
|
||||
xml2js: 0.6.2
|
||||
xss: 1.0.15
|
||||
zod: 3.24.1
|
||||
'zod-to-json-schema': 3.23.3
|
||||
zod-to-json-schema: 3.23.3
|
||||
'@langchain/core': 0.3.48
|
||||
'@langchain/openai': 0.5.0
|
||||
'@langchain/anthropic': 0.3.21
|
||||
@@ -66,4 +66,4 @@ catalogs:
|
||||
vue-tsc: ^2.2.8
|
||||
vue-markdown-render: ^2.2.1
|
||||
highlight.js: ^11.8.0
|
||||
'element-plus': 2.4.3
|
||||
element-plus: 2.4.3
|
||||
|
||||
Reference in New Issue
Block a user