From 7068fe2510882c438cbd73bede40dbbb1a1eebbb Mon Sep 17 00:00:00 2001 From: Nikhil Kuriakose Date: Tue, 25 Nov 2025 11:23:32 +0100 Subject: [PATCH] fix(core): Send prod workflow succeeded for wfs in projects (#22223) --- .../__tests__/telemetry-event-relay.test.ts | 18 +++++++++- .../cli/src/events/maps/relay.event-map.ts | 2 +- .../events/relays/telemetry.event-relay.ts | 2 +- ...low-statistics.service.integration.test.ts | 35 ++++++++++++++++++- .../services/workflow-statistics.service.ts | 15 ++++---- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts index 0e021f4c17b..77e714d7a2f 100644 --- a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts +++ b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts @@ -1155,7 +1155,7 @@ describe('TelemetryEventRelay', () => { }); describe('workflow execution events', () => { - it('should track on `first-production-workflow-succeeded` event', () => { + it('should track on `first-production-workflow-succeeded` event for personal project', () => { const event: RelayEventMap['first-production-workflow-succeeded'] = { projectId: 'project123', workflowId: 'workflow123', @@ -1171,6 +1171,22 @@ describe('TelemetryEventRelay', () => { }); }); + it('should track on `first-production-workflow-succeeded` event for team project with null userId', () => { + const event: RelayEventMap['first-production-workflow-succeeded'] = { + projectId: 'project123', + workflowId: 'workflow123', + userId: null, + }; + + eventService.emit('first-production-workflow-succeeded', event); + + expect(telemetry.track).toHaveBeenCalledWith('Workflow first prod success', { + project_id: 'project123', + workflow_id: 'workflow123', + user_id: undefined, + }); + }); + it('should track on `first-workflow-data-loaded` event', () => { const event: RelayEventMap['first-workflow-data-loaded'] = { userId: 'user123', diff --git a/packages/cli/src/events/maps/relay.event-map.ts b/packages/cli/src/events/maps/relay.event-map.ts index e7c8d32542a..9713d387c09 100644 --- a/packages/cli/src/events/maps/relay.event-map.ts +++ b/packages/cli/src/events/maps/relay.event-map.ts @@ -39,7 +39,7 @@ export type RelayEventMap = { 'first-production-workflow-succeeded': { projectId: string; workflowId: string; - userId: string; + userId: string | null; }; 'first-workflow-data-loaded': { diff --git a/packages/cli/src/events/relays/telemetry.event-relay.ts b/packages/cli/src/events/relays/telemetry.event-relay.ts index c22373d6e6c..9bfc150eefb 100644 --- a/packages/cli/src/events/relays/telemetry.event-relay.ts +++ b/packages/cli/src/events/relays/telemetry.event-relay.ts @@ -936,7 +936,7 @@ export class TelemetryEventRelay extends EventRelay { this.telemetry.track('Workflow first prod success', { project_id: projectId, workflow_id: workflowId, - user_id: userId, + user_id: userId ?? undefined, }); } diff --git a/packages/cli/src/services/__tests__/workflow-statistics.service.integration.test.ts b/packages/cli/src/services/__tests__/workflow-statistics.service.integration.test.ts index 61477eded6c..9a2b497b25d 100644 --- a/packages/cli/src/services/__tests__/workflow-statistics.service.integration.test.ts +++ b/packages/cli/src/services/__tests__/workflow-statistics.service.integration.test.ts @@ -1,4 +1,10 @@ -import { getPersonalProject, createWorkflow, testDb, mockInstance } from '@n8n/backend-test-utils'; +import { + getPersonalProject, + createTeamProject, + createWorkflow, + testDb, + mockInstance, +} from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; import type { IWorkflowDb, Project, WorkflowEntity, User } from '@n8n/db'; import { WorkflowStatisticsRepository } from '@n8n/db'; @@ -243,6 +249,33 @@ describe('WorkflowStatisticsService', () => { expect(updateSettingsSpy).not.toHaveBeenCalled(); expect(emitSpy).not.toHaveBeenCalled(); }); + + test('emits first-production-workflow-succeeded with null userId for team project', async () => { + // ARRANGE + const teamProject = await createTeamProject('Team Project'); + const teamWorkflow = await createWorkflow({}, teamProject); + const runData: IRun = { + finished: true, + status: 'success', + data: createEmptyRunExecutionData(), + mode: 'internal', + startedAt: new Date(), + }; + const emitSpy = jest.spyOn(Container.get(EventService), 'emit'); + const updateSettingsSpy = jest.spyOn(userService, 'updateSettings'); + + // ACT + await workflowStatisticsService.workflowExecutionCompleted(teamWorkflow, runData); + + // ASSERT + expect(updateSettingsSpy).not.toHaveBeenCalled(); + expect(emitSpy).toHaveBeenCalledTimes(1); + expect(emitSpy).toHaveBeenCalledWith('first-production-workflow-succeeded', { + projectId: teamProject.id, + workflowId: teamWorkflow.id, + userId: null, + }); + }); }); describe('nodeFetchedData', () => { diff --git a/packages/cli/src/services/workflow-statistics.service.ts b/packages/cli/src/services/workflow-statistics.service.ts index fdfbadedf9d..fc94468a4c7 100644 --- a/packages/cli/src/services/workflow-statistics.service.ts +++ b/packages/cli/src/services/workflow-statistics.service.ts @@ -116,8 +116,11 @@ export class WorkflowStatisticsService extends TypedEmitter