From 741dd693a474d905e73a90214aa143d5ca26af7f Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 18 Aug 2025 17:49:21 +0200 Subject: [PATCH] fix(core): Handle insights by workflow table for deleted workflows (#18496) --- .../schemas/__tests__/insights.schema.test.ts | 40 +++++++++++++------ .../api-types/src/schemas/insights.schema.ts | 6 ++- .../insights-by-period.repository.ts | 4 +- .../tables/InsightsTableWorkflows.vue | 19 ++++++--- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/packages/@n8n/api-types/src/schemas/__tests__/insights.schema.test.ts b/packages/@n8n/api-types/src/schemas/__tests__/insights.schema.test.ts index e2f4e678a2f..5c8253729a6 100644 --- a/packages/@n8n/api-types/src/schemas/__tests__/insights.schema.test.ts +++ b/packages/@n8n/api-types/src/schemas/__tests__/insights.schema.test.ts @@ -69,24 +69,40 @@ describe('insightsSummarySchema', () => { }); describe('insightsByWorkflowSchema', () => { + const validInsightsByWorkflow = { + count: 2, + data: [ + { + workflowId: 'w1', + workflowName: 'Test Workflow', + projectId: 'p1', + projectName: 'Test Project', + total: 100, + succeeded: 90, + failed: 10, + failureRate: 0.56, + runTime: 300, + averageRunTime: 30.5, + timeSaved: 50, + }, + ], + }; + test.each([ { name: 'valid workflow insights', + value: validInsightsByWorkflow, + expected: true, + }, + { + name: 'workflow insights with nullable workflow id and project id', value: { - count: 2, + ...validInsightsByWorkflow, data: [ { - workflowId: 'w1', - workflowName: 'Test Workflow', - projectId: 'p1', - projectName: 'Test Project', - total: 100, - succeeded: 90, - failed: 10, - failureRate: 0.56, - runTime: 300, - averageRunTime: 30.5, - timeSaved: 50, + ...validInsightsByWorkflow.data[0], + workflowId: null, + projectId: null, }, ], }, diff --git a/packages/@n8n/api-types/src/schemas/insights.schema.ts b/packages/@n8n/api-types/src/schemas/insights.schema.ts index 4c3195f339e..59f5fe6a10d 100644 --- a/packages/@n8n/api-types/src/schemas/insights.schema.ts +++ b/packages/@n8n/api-types/src/schemas/insights.schema.ts @@ -48,9 +48,11 @@ export const insightsByWorkflowDataSchemas = { data: z.array( z .object({ - workflowId: z.string(), + // Workflow id will be null if the workflow has been deleted + workflowId: z.string().nullable(), workflowName: z.string(), - projectId: z.string(), + // Project id will be null if the project has been deleted + projectId: z.string().nullable(), projectName: z.string(), total: z.number(), succeeded: z.number(), diff --git a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts index e7e65a0f718..d0dac1cd5da 100644 --- a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts +++ b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts @@ -30,9 +30,9 @@ const summaryParser = z const aggregatedInsightsByWorkflowParser = z .object({ - workflowId: z.string(), + workflowId: z.string().nullable(), workflowName: z.string(), - projectId: z.string(), + projectId: z.string().nullable(), projectName: z.string(), total: z.union([z.number(), z.string()]).transform((value) => Number(value)), succeeded: z.union([z.number(), z.string()]).transform((value) => Number(value)), diff --git a/packages/frontend/editor-ui/src/features/insights/components/tables/InsightsTableWorkflows.vue b/packages/frontend/editor-ui/src/features/insights/components/tables/InsightsTableWorkflows.vue index 11ec62d3bd8..016aaf4472a 100644 --- a/packages/frontend/editor-ui/src/features/insights/components/tables/InsightsTableWorkflows.vue +++ b/packages/frontend/editor-ui/src/features/insights/components/tables/InsightsTableWorkflows.vue @@ -168,21 +168,28 @@ watch(sortBy, (newValue) => { @update:options="emit('update:options', $event)" >