From 52ad1857748cf83095e6a45b14ff37bb1d31f6da Mon Sep 17 00:00:00 2001 From: Federico Meini Date: Mon, 21 Oct 2024 18:56:25 +0200 Subject: [PATCH 1/2] Record sessionStarted telemetry event in Setting Store --- .../src/stores/settings.store.test.ts | 257 ++++++++++++++++++ .../editor-ui/src/stores/settings.store.ts | 5 + 2 files changed, 262 insertions(+) create mode 100644 packages/editor-ui/src/stores/settings.store.test.ts diff --git a/packages/editor-ui/src/stores/settings.store.test.ts b/packages/editor-ui/src/stores/settings.store.test.ts new file mode 100644 index 0000000000000..6d022e33c9348 --- /dev/null +++ b/packages/editor-ui/src/stores/settings.store.test.ts @@ -0,0 +1,257 @@ +import type { FrontendSettings } from '@n8n/api-types'; +import { useSettingsStore } from './settings.store'; +import { createPinia, setActivePinia } from 'pinia'; + +const { getSettings } = vi.hoisted(() => ({ + getSettings: vi.fn(), +})); + +const { sessionStarted } = vi.hoisted(() => ({ + sessionStarted: vi.fn(), +})); + +vi.mock('@/api/settings', () => ({ + getSettings, +})); + +vi.mock('@/api/events', () => ({ + sessionStarted, +})); + +vi.mock('@/stores/root.store', () => ({ + useRootStore: vi.fn(() => ({ + restApiContext: {}, + setVersionCli: vi.fn(), + })), +})); + +vi.mock('@/stores/root.store', () => ({ + useRootStore: vi.fn(() => ({ + setUrlBaseWebhook: vi.fn(), + setUrlBaseEditor: vi.fn(), + setEndpointForm: vi.fn(), + setEndpointFormTest: vi.fn(), + setEndpointFormWaiting: vi.fn(), + setEndpointWebhook: vi.fn(), + setEndpointWebhookTest: vi.fn(), + setEndpointWebhookWaiting: vi.fn(), + setTimezone: vi.fn(), + setExecutionTimeout: vi.fn(), + setMaxExecutionTimeout: vi.fn(), + setInstanceId: vi.fn(), + setOauthCallbackUrls: vi.fn(), + setN8nMetadata: vi.fn(), + setDefaultLocale: vi.fn(), + setBinaryDataMode: vi.fn(), + setVersionCli: vi.fn(), + })), +})); + +vi.mock('@/stores/versions.store', () => ({ + useVersionsStore: vi.fn(() => ({ + setVersionNotificationSettings: vi.fn(), + })), +})); + +const mockSettings: FrontendSettings = { + isDocker: true, + databaseType: 'postgresdb', + endpointForm: '/form', + endpointFormTest: '/form-test', + endpointFormWaiting: '/form-waiting', + endpointWebhook: '/webhook', + endpointWebhookTest: '/webhook-test', + endpointWebhookWaiting: '/webhook-waiting', + saveDataErrorExecution: 'all', + saveDataSuccessExecution: 'none', + saveManualExecutions: true, + saveExecutionProgress: true, + executionTimeout: 3600, + maxExecutionTimeout: 7200, + workflowCallerPolicyDefaultOption: 'any', + oauthCallbackUrls: { + oauth1: '/oauth1-callback', + oauth2: '/oauth2-callback', + }, + timezone: 'UTC', + urlBaseWebhook: 'https://example.com/webhook', + urlBaseEditor: 'https://example.com/editor', + versionCli: '1.0.0', + nodeJsVersion: '14.17.0', + concurrency: 10, + authCookie: { + secure: true, + }, + binaryDataMode: 'filesystem', + releaseChannel: 'stable', + n8nMetadata: { + userId: '123', + }, + versionNotifications: { + enabled: true, + endpoint: '/version', + infoUrl: 'https://example.com/info', + }, + instanceId: 'test-instance-id', + telemetry: { + enabled: true, + config: { + url: 'https://telemetry.example.com', + key: 'telemetry-key', + }, + }, + posthog: { + enabled: true, + apiHost: 'https://api.posthog.com', + apiKey: 'posthog-key', + autocapture: true, + disableSessionRecording: false, + debug: false, + }, + personalizationSurveyEnabled: true, + defaultLocale: 'en', + userManagement: { + quota: 100, + showSetupOnFirstLoad: false, + smtpSetup: true, + authenticationMethod: 'email', + }, + sso: { + saml: { + loginLabel: 'SAML Login', + loginEnabled: false, + }, + ldap: { + loginLabel: 'LDAP Login', + loginEnabled: true, + }, + }, + publicApi: { + enabled: true, + latestVersion: 1, + path: '/api', + swaggerUi: { + enabled: true, + }, + }, + workflowTagsDisabled: false, + logLevel: 'debug', + hiringBannerEnabled: true, + previewMode: false, + templates: { + enabled: true, + host: 'https://templates.example.com', + }, + missingPackages: false, + executionMode: 'queue', + pushBackend: 'sse', + communityNodesEnabled: true, + aiAssistant: { + enabled: false, + }, + askAi: { + enabled: false, + }, + deployment: { + type: 'cloud', + }, + allowedModules: { + builtIn: ['n8n-nodes-base'], + external: ['n8n-nodes-custom'], + }, + enterprise: { + sharing: true, + ldap: true, + saml: true, + logStreaming: true, + advancedExecutionFilters: true, + variables: true, + sourceControl: false, + auditLogs: true, + externalSecrets: true, + showNonProdBanner: false, + debugInEditor: true, + binaryDataS3: false, + workflowHistory: true, + workerView: true, + advancedPermissions: true, + projects: { + team: { + limit: 10, + }, + }, + }, + hideUsagePage: false, + license: { + planName: 'Community', + consumerId: 'consumer-123', + environment: 'production', + }, + variables: { + limit: 100, + }, + expressions: { + evaluator: 'tournament', + }, + mfa: { + enabled: true, + }, + banners: { + dismissed: ['V1', 'V2'], + }, + workflowHistory: { + pruneTime: 30, + licensePruneTime: 60, + }, + pruning: { + isEnabled: true, + maxAge: 30, + maxCount: 1000, + }, + security: { + blockFileAccessToN8nFiles: true, + }, +}; + +describe('settings.store', () => { + beforeEach(() => { + vi.restoreAllMocks(); + setActivePinia(createPinia()); + }); + + describe('getSettings', () => { + it('should fetch settings and call sessionStarted if telemetry is enabled', async () => { + const settingsStore = useSettingsStore(); + + getSettings.mockResolvedValueOnce({ + ...mockSettings, + telemetry: { + enabled: true, + config: { + url: 'https://telemetry.example.com', + key: 'telemetry-key', + }, + }, + }); + + await settingsStore.getSettings(); + expect(getSettings).toHaveBeenCalled(); + expect(sessionStarted).toHaveBeenCalled(); + }); + }); + + it('should fetch settings and skip calling sessionStarted if telemetry is disabled', async () => { + const settingsStore = useSettingsStore(); + + getSettings.mockResolvedValueOnce({ + ...mockSettings, + telemetry: { + enabled: false, + }, + }); + + await settingsStore.getSettings(); + expect(getSettings).toHaveBeenCalled(); + expect(sessionStarted).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index 321ed5b437225..b7325e4ec5eef 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -3,6 +3,7 @@ import Bowser from 'bowser'; import type { IUserManagementSettings, FrontendSettings } from '@n8n/api-types'; import * as publicApiApi from '@/api/api-keys'; +import * as eventsApi from '@/api/events'; import * as ldapApi from '@/api/ldap'; import * as settingsApi from '@/api/settings'; import { testHealthEndpoint } from '@/api/templates'; @@ -247,6 +248,10 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => { rootStore.setDefaultLocale(fetchedSettings.defaultLocale); rootStore.setBinaryDataMode(fetchedSettings.binaryDataMode); useVersionsStore().setVersionNotificationSettings(fetchedSettings.versionNotifications); + + if (fetchedSettings.telemetry.enabled) { + void eventsApi.sessionStarted(rootStore.restApiContext); + } }; const initialize = async () => { From 36a8a99190f320cacaa8b44c52548abd41325d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 21 Oct 2024 20:08:50 +0200 Subject: [PATCH 2/2] use a mock for the settings object --- .../src/stores/settings.store.test.ts | 189 ++---------------- 1 file changed, 17 insertions(+), 172 deletions(-) diff --git a/packages/editor-ui/src/stores/settings.store.test.ts b/packages/editor-ui/src/stores/settings.store.test.ts index 6d022e33c9348..147d24f684b67 100644 --- a/packages/editor-ui/src/stores/settings.store.test.ts +++ b/packages/editor-ui/src/stores/settings.store.test.ts @@ -1,6 +1,7 @@ import type { FrontendSettings } from '@n8n/api-types'; -import { useSettingsStore } from './settings.store'; import { createPinia, setActivePinia } from 'pinia'; +import { mock } from 'vitest-mock-extended'; +import { useSettingsStore } from './settings.store'; const { getSettings } = vi.hoisted(() => ({ getSettings: vi.fn(), @@ -53,165 +54,9 @@ vi.mock('@/stores/versions.store', () => ({ })), })); -const mockSettings: FrontendSettings = { - isDocker: true, - databaseType: 'postgresdb', - endpointForm: '/form', - endpointFormTest: '/form-test', - endpointFormWaiting: '/form-waiting', - endpointWebhook: '/webhook', - endpointWebhookTest: '/webhook-test', - endpointWebhookWaiting: '/webhook-waiting', - saveDataErrorExecution: 'all', - saveDataSuccessExecution: 'none', - saveManualExecutions: true, - saveExecutionProgress: true, - executionTimeout: 3600, - maxExecutionTimeout: 7200, - workflowCallerPolicyDefaultOption: 'any', - oauthCallbackUrls: { - oauth1: '/oauth1-callback', - oauth2: '/oauth2-callback', - }, - timezone: 'UTC', - urlBaseWebhook: 'https://example.com/webhook', - urlBaseEditor: 'https://example.com/editor', - versionCli: '1.0.0', - nodeJsVersion: '14.17.0', - concurrency: 10, - authCookie: { - secure: true, - }, - binaryDataMode: 'filesystem', - releaseChannel: 'stable', - n8nMetadata: { - userId: '123', - }, - versionNotifications: { - enabled: true, - endpoint: '/version', - infoUrl: 'https://example.com/info', - }, - instanceId: 'test-instance-id', - telemetry: { - enabled: true, - config: { - url: 'https://telemetry.example.com', - key: 'telemetry-key', - }, - }, - posthog: { - enabled: true, - apiHost: 'https://api.posthog.com', - apiKey: 'posthog-key', - autocapture: true, - disableSessionRecording: false, - debug: false, - }, - personalizationSurveyEnabled: true, - defaultLocale: 'en', - userManagement: { - quota: 100, - showSetupOnFirstLoad: false, - smtpSetup: true, - authenticationMethod: 'email', - }, - sso: { - saml: { - loginLabel: 'SAML Login', - loginEnabled: false, - }, - ldap: { - loginLabel: 'LDAP Login', - loginEnabled: true, - }, - }, - publicApi: { - enabled: true, - latestVersion: 1, - path: '/api', - swaggerUi: { - enabled: true, - }, - }, - workflowTagsDisabled: false, - logLevel: 'debug', - hiringBannerEnabled: true, - previewMode: false, - templates: { - enabled: true, - host: 'https://templates.example.com', - }, - missingPackages: false, - executionMode: 'queue', - pushBackend: 'sse', - communityNodesEnabled: true, - aiAssistant: { - enabled: false, - }, - askAi: { - enabled: false, - }, - deployment: { - type: 'cloud', - }, - allowedModules: { - builtIn: ['n8n-nodes-base'], - external: ['n8n-nodes-custom'], - }, - enterprise: { - sharing: true, - ldap: true, - saml: true, - logStreaming: true, - advancedExecutionFilters: true, - variables: true, - sourceControl: false, - auditLogs: true, - externalSecrets: true, - showNonProdBanner: false, - debugInEditor: true, - binaryDataS3: false, - workflowHistory: true, - workerView: true, - advancedPermissions: true, - projects: { - team: { - limit: 10, - }, - }, - }, - hideUsagePage: false, - license: { - planName: 'Community', - consumerId: 'consumer-123', - environment: 'production', - }, - variables: { - limit: 100, - }, - expressions: { - evaluator: 'tournament', - }, - mfa: { - enabled: true, - }, - banners: { - dismissed: ['V1', 'V2'], - }, - workflowHistory: { - pruneTime: 30, - licensePruneTime: 60, - }, - pruning: { - isEnabled: true, - maxAge: 30, - maxCount: 1000, - }, - security: { - blockFileAccessToN8nFiles: true, - }, -}; +const mockSettings = mock({ + authCookie: { secure: true }, +}); describe('settings.store', () => { beforeEach(() => { @@ -238,20 +83,20 @@ describe('settings.store', () => { expect(getSettings).toHaveBeenCalled(); expect(sessionStarted).toHaveBeenCalled(); }); - }); - it('should fetch settings and skip calling sessionStarted if telemetry is disabled', async () => { - const settingsStore = useSettingsStore(); + it('should fetch settings and skip calling sessionStarted if telemetry is disabled', async () => { + const settingsStore = useSettingsStore(); - getSettings.mockResolvedValueOnce({ - ...mockSettings, - telemetry: { - enabled: false, - }, - }); + getSettings.mockResolvedValueOnce({ + ...mockSettings, + telemetry: { + enabled: false, + }, + }); - await settingsStore.getSettings(); - expect(getSettings).toHaveBeenCalled(); - expect(sessionStarted).not.toHaveBeenCalled(); + await settingsStore.getSettings(); + expect(getSettings).toHaveBeenCalled(); + expect(sessionStarted).not.toHaveBeenCalled(); + }); }); });