From 475d72e0bc9e13c6dc56129902f6f89c67547f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20G=C3=B3mez=20Morales?= Date: Thu, 24 Oct 2024 08:57:32 +0200 Subject: [PATCH 01/13] fix(editor): Fix Cannot read properties of undefined (reading 'finished') (#11367) --- .../src/stores/workflows.store.spec.ts | 52 ++++++++++++++++++- .../editor-ui/src/stores/workflows.store.ts | 26 ++++------ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/packages/editor-ui/src/stores/workflows.store.spec.ts b/packages/editor-ui/src/stores/workflows.store.spec.ts index 1c3fbf9e8f..acf86d0744 100644 --- a/packages/editor-ui/src/stores/workflows.store.spec.ts +++ b/packages/editor-ui/src/stores/workflows.store.spec.ts @@ -8,7 +8,13 @@ import { WAIT_NODE_TYPE, } from '@/constants'; import { useWorkflowsStore } from '@/stores/workflows.store'; -import type { IExecutionResponse, INodeUi, IWorkflowDb, IWorkflowSettings } from '@/Interface'; +import type { + IExecutionResponse, + IExecutionsCurrentSummaryExtended, + INodeUi, + IWorkflowDb, + IWorkflowSettings, +} from '@/Interface'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { @@ -594,6 +600,50 @@ describe('useWorkflowsStore', () => { ); }); }); + + describe('finishActiveExecution', () => { + it('should update execution', async () => { + const cursor = 1; + const ids = ['0', '1', '2']; + workflowsStore.setActiveExecutions( + ids.map((id) => ({ id })) as IExecutionsCurrentSummaryExtended[], + ); + + const stoppedAt = new Date(); + + workflowsStore.finishActiveExecution({ + executionId: ids[cursor], + data: { + finished: true, + stoppedAt, + }, + } as PushPayload<'executionFinished'>); + + expect(workflowsStore.activeExecutions[cursor]).toStrictEqual({ + id: ids[cursor], + finished: true, + stoppedAt, + }); + }); + + it('should handle parameter casting', async () => { + const cursor = 1; + const ids = ['0', '1', '2']; + workflowsStore.setActiveExecutions( + ids.map((id) => ({ id })) as IExecutionsCurrentSummaryExtended[], + ); + + workflowsStore.finishActiveExecution({ + executionId: ids[cursor], + } as PushPayload<'executionFinished'>); + + expect(workflowsStore.activeExecutions[cursor]).toStrictEqual({ + id: ids[cursor], + finished: undefined, + stoppedAt: undefined, + }); + }); + }); }); function getMockEditFieldsNode() { diff --git a/packages/editor-ui/src/stores/workflows.store.ts b/packages/editor-ui/src/stores/workflows.store.ts index b93b00106a..08bee0d448 100644 --- a/packages/editor-ui/src/stores/workflows.store.ts +++ b/packages/editor-ui/src/stores/workflows.store.ts @@ -47,7 +47,6 @@ import type { INodeParameters, INodeTypes, IPinData, - IRun, IRunData, IRunExecutionData, ITaskData, @@ -1344,23 +1343,16 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { return; } - const activeExecution = activeExecutions.value[activeExecutionIndex]; + Object.assign(activeExecutions.value[activeExecutionIndex], { + ...(finishedActiveExecution.executionId !== undefined + ? { id: finishedActiveExecution.executionId } + : {}), + finished: finishedActiveExecution.data?.finished, + stoppedAt: finishedActiveExecution.data?.stoppedAt, + }); - activeExecutions.value = [ - ...activeExecutions.value.slice(0, activeExecutionIndex), - { - ...activeExecution, - ...(finishedActiveExecution.executionId !== undefined - ? { id: finishedActiveExecution.executionId } - : {}), - finished: finishedActiveExecution.data.finished, - stoppedAt: finishedActiveExecution.data.stoppedAt, - }, - ...activeExecutions.value.slice(activeExecutionIndex + 1), - ]; - - if (finishedActiveExecution.data && (finishedActiveExecution.data as IRun).data) { - setWorkflowExecutionRunData((finishedActiveExecution.data as IRun).data); + if (finishedActiveExecution.data?.data) { + setWorkflowExecutionRunData(finishedActiveExecution.data.data); } } From 88c1c4ad7b554f47285a10f293c3a903208270c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 24 Oct 2024 11:37:19 +0200 Subject: [PATCH 02/13] refactor(core): Add external secrets log scope (#11224) --- .../@n8n/config/src/configs/logging.config.ts | 2 ++ .../external-secrets-manager.ee.test.ts | 4 ++-- .../external-secrets-manager.ee.ts | 22 ++++++++++++++++--- .../aws-secrets/aws-secrets-manager.ts | 18 ++++++++++++++- .../azure-key-vault/azure-key-vault.ts | 17 +++++++++++++- .../gcp-secrets-manager.ts | 16 ++++++++++++-- .../src/external-secrets/providers/vault.ts | 7 ++++++ .../external-secrets.api.test.ts | 18 +++++++++++++-- 8 files changed, 93 insertions(+), 11 deletions(-) diff --git a/packages/@n8n/config/src/configs/logging.config.ts b/packages/@n8n/config/src/configs/logging.config.ts index 736e32a903..0568eaf791 100644 --- a/packages/@n8n/config/src/configs/logging.config.ts +++ b/packages/@n8n/config/src/configs/logging.config.ts @@ -4,6 +4,7 @@ import { StringArray } from '../utils'; /** Scopes (areas of functionality) to filter logs by. */ export const LOG_SCOPES = [ 'concurrency', + 'external-secrets', 'license', 'multi-main-setup', 'pubsub', @@ -64,6 +65,7 @@ export class LoggingConfig { * Supported log scopes: * * - `concurrency` + * - `external-secrets` * - `license` * - `multi-main-setup` * - `pubsub` diff --git a/packages/cli/src/external-secrets/__tests__/external-secrets-manager.ee.test.ts b/packages/cli/src/external-secrets/__tests__/external-secrets-manager.ee.test.ts index b1a87271f9..05eabd104f 100644 --- a/packages/cli/src/external-secrets/__tests__/external-secrets-manager.ee.test.ts +++ b/packages/cli/src/external-secrets/__tests__/external-secrets-manager.ee.test.ts @@ -13,7 +13,7 @@ import { FailedProvider, MockProviders, } from '@test/external-secrets/utils'; -import { mockInstance } from '@test/mocking'; +import { mockInstance, mockLogger } from '@test/mocking'; describe('External Secrets Manager', () => { const connectedDate = '2023-08-01T12:32:29.000Z'; @@ -49,7 +49,7 @@ describe('External Secrets Manager', () => { license.isExternalSecretsEnabled.mockReturnValue(true); settingsRepo.getEncryptedSecretsProviderSettings.mockResolvedValue(settings); manager = new ExternalSecretsManager( - mock(), + mockLogger(), settingsRepo, license, providersMock, diff --git a/packages/cli/src/external-secrets/external-secrets-manager.ee.ts b/packages/cli/src/external-secrets/external-secrets-manager.ee.ts index ec7c3ed0cf..2de681a7d6 100644 --- a/packages/cli/src/external-secrets/external-secrets-manager.ee.ts +++ b/packages/cli/src/external-secrets/external-secrets-manager.ee.ts @@ -1,5 +1,5 @@ import { Cipher } from 'n8n-core'; -import { jsonParse, type IDataObject, ApplicationError } from 'n8n-workflow'; +import { jsonParse, type IDataObject, ApplicationError, ensureError } from 'n8n-workflow'; import { Service } from 'typedi'; import { SettingsRepository } from '@/databases/repositories/settings.repository'; @@ -39,7 +39,9 @@ export class ExternalSecretsManager { private readonly cipher: Cipher, private readonly eventService: EventService, private readonly publisher: Publisher, - ) {} + ) { + this.logger = this.logger.scoped('external-secrets'); + } async init(): Promise { if (!this.initialized) { @@ -57,6 +59,8 @@ export class ExternalSecretsManager { } return await this.initializingPromise; } + + this.logger.debug('External secrets manager initialized'); } shutdown() { @@ -66,6 +70,8 @@ export class ExternalSecretsManager { void p.disconnect().catch(() => {}); }); Object.values(this.initRetryTimeouts).forEach((v) => clearTimeout(v)); + + this.logger.debug('External secrets manager shut down'); } async reloadAllProviders(backoff?: number) { @@ -77,6 +83,8 @@ export class ExternalSecretsManager { for (const provider of providers) { await this.reloadProvider(provider, backoff); } + + this.logger.debug('External secrets managed reloaded all providers'); } broadcastReloadExternalSecretsProviders() { @@ -191,6 +199,8 @@ export class ExternalSecretsManager { } }), ); + + this.logger.debug('External secrets manager updated secrets'); } getProvider(provider: string): SecretsProvider | undefined { @@ -261,6 +271,8 @@ export class ExternalSecretsManager { if (newProvider) { this.providers[provider] = newProvider; } + + this.logger.debug(`External secrets manager reloaded provider ${provider}`); } async setProviderSettings(provider: string, data: IDataObject, userId?: string) { @@ -382,8 +394,12 @@ export class ExternalSecretsManager { try { await this.providers[provider].update(); this.broadcastReloadExternalSecretsProviders(); + this.logger.debug(`External secrets manager updated provider ${provider}`); return true; - } catch { + } catch (error) { + this.logger.debug(`External secrets manager failed to update provider ${provider}`, { + error: ensureError(error), + }); return false; } } diff --git a/packages/cli/src/external-secrets/providers/aws-secrets/aws-secrets-manager.ts b/packages/cli/src/external-secrets/providers/aws-secrets/aws-secrets-manager.ts index 2c17aee5a6..6c2c0669fb 100644 --- a/packages/cli/src/external-secrets/providers/aws-secrets/aws-secrets-manager.ts +++ b/packages/cli/src/external-secrets/providers/aws-secrets/aws-secrets-manager.ts @@ -1,8 +1,10 @@ import type { INodeProperties } from 'n8n-workflow'; +import Container from 'typedi'; import { UnknownAuthTypeError } from '@/errors/unknown-auth-type.error'; import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '@/external-secrets/constants'; import type { SecretsProvider, SecretsProviderState } from '@/interfaces'; +import { Logger } from '@/logging/logger.service'; import { AwsSecretsClient } from './aws-secrets-client'; import type { AwsSecretsManagerContext } from './types'; @@ -76,10 +78,16 @@ export class AwsSecretsManager implements SecretsProvider { private client: AwsSecretsClient; + constructor(private readonly logger = Container.get(Logger)) { + this.logger = this.logger.scoped('external-secrets'); + } + async init(context: AwsSecretsManagerContext) { this.assertAuthType(context); this.client = new AwsSecretsClient(context.settings); + + this.logger.debug('AWS Secrets Manager provider initialized'); } async test() { @@ -87,9 +95,15 @@ export class AwsSecretsManager implements SecretsProvider { } async connect() { - const [wasSuccessful] = await this.test(); + const [wasSuccessful, errorMsg] = await this.test(); this.state = wasSuccessful ? 'connected' : 'error'; + + if (wasSuccessful) { + this.logger.debug('AWS Secrets Manager provider connected'); + } else { + this.logger.error('AWS Secrets Manager provider failed to connect', { errorMsg }); + } } async disconnect() { @@ -104,6 +118,8 @@ export class AwsSecretsManager implements SecretsProvider { this.cachedSecrets = Object.fromEntries( supportedSecrets.map((s) => [s.secretName, s.secretValue]), ); + + this.logger.debug('AWS Secrets Manager provider secrets updated'); } getSecret(name: string) { diff --git a/packages/cli/src/external-secrets/providers/azure-key-vault/azure-key-vault.ts b/packages/cli/src/external-secrets/providers/azure-key-vault/azure-key-vault.ts index e753f0abbf..7961f21bad 100644 --- a/packages/cli/src/external-secrets/providers/azure-key-vault/azure-key-vault.ts +++ b/packages/cli/src/external-secrets/providers/azure-key-vault/azure-key-vault.ts @@ -1,8 +1,11 @@ import type { SecretClient } from '@azure/keyvault-secrets'; +import { ensureError } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow'; +import Container from 'typedi'; import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '@/external-secrets/constants'; import type { SecretsProvider, SecretsProviderState } from '@/interfaces'; +import { Logger } from '@/logging/logger.service'; import type { AzureKeyVaultContext } from './types'; @@ -64,8 +67,14 @@ export class AzureKeyVault implements SecretsProvider { private settings: AzureKeyVaultContext['settings']; + constructor(private readonly logger = Container.get(Logger)) { + this.logger = this.logger.scoped('external-secrets'); + } + async init(context: AzureKeyVaultContext) { this.settings = context.settings; + + this.logger.debug('Azure Key Vault provider initialized'); } async connect() { @@ -78,8 +87,12 @@ export class AzureKeyVault implements SecretsProvider { const credential = new ClientSecretCredential(tenantId, clientId, clientSecret); this.client = new SecretClient(`https://${vaultName}.vault.azure.net/`, credential); this.state = 'connected'; - } catch { + this.logger.debug('Azure Key Vault provider connected'); + } catch (error) { this.state = 'error'; + this.logger.error('Azure Key Vault provider failed to connect', { + error: ensureError(error), + }); } } @@ -119,6 +132,8 @@ export class AzureKeyVault implements SecretsProvider { acc[cur.name] = cur.value; return acc; }, {}); + + this.logger.debug('Azure Key Vault provider secrets updated'); } getSecret(name: string) { diff --git a/packages/cli/src/external-secrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts b/packages/cli/src/external-secrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts index e6bcd11209..c4bf71cb72 100644 --- a/packages/cli/src/external-secrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts +++ b/packages/cli/src/external-secrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts @@ -1,8 +1,10 @@ import type { SecretManagerServiceClient as GcpClient } from '@google-cloud/secret-manager'; -import { jsonParse, type INodeProperties } from 'n8n-workflow'; +import { ensureError, jsonParse, type INodeProperties } from 'n8n-workflow'; +import Container from 'typedi'; import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '@/external-secrets/constants'; import type { SecretsProvider, SecretsProviderState } from '@/interfaces'; +import { Logger } from '@/logging/logger.service'; import type { GcpSecretsManagerContext, @@ -38,6 +40,10 @@ export class GcpSecretsManager implements SecretsProvider { private settings: GcpSecretAccountKey; + constructor(private readonly logger = Container.get(Logger)) { + this.logger = this.logger.scoped('external-secrets'); + } + async init(context: GcpSecretsManagerContext) { this.settings = this.parseSecretAccountKey(context.settings.serviceAccountKey); } @@ -53,8 +59,12 @@ export class GcpSecretsManager implements SecretsProvider { projectId, }); this.state = 'connected'; - } catch { + this.logger.debug('GCP Secrets Manager provider connected'); + } catch (error) { this.state = 'error'; + this.logger.debug('GCP Secrets Manager provider failed to connect', { + error: ensureError(error), + }); } } @@ -114,6 +124,8 @@ export class GcpSecretsManager implements SecretsProvider { if (cur) acc[cur.name] = cur.value; return acc; }, {}); + + this.logger.debug('GCP Secrets Manager provider secrets updated'); } getSecret(name: string) { diff --git a/packages/cli/src/external-secrets/providers/vault.ts b/packages/cli/src/external-secrets/providers/vault.ts index 398c40745d..0f1e93a5da 100644 --- a/packages/cli/src/external-secrets/providers/vault.ts +++ b/packages/cli/src/external-secrets/providers/vault.ts @@ -237,6 +237,7 @@ export class VaultProvider extends SecretsProvider { constructor(readonly logger = Container.get(Logger)) { super(); + this.logger = this.logger.scoped('external-secrets'); } async init(settings: SecretsProviderSettings): Promise { @@ -257,6 +258,8 @@ export class VaultProvider extends SecretsProvider { } return config; }); + + this.logger.debug('Vault provider initialized'); } async connect(): Promise { @@ -408,6 +411,7 @@ export class VaultProvider extends SecretsProvider { kvVersion: string, path: string, ): Promise<[string, IDataObject] | null> { + this.logger.debug(`Getting kv secrets from ${mountPath}${path} (version ${kvVersion})`); let listPath = mountPath; if (kvVersion === '2') { listPath += 'metadata/'; @@ -441,6 +445,7 @@ export class VaultProvider extends SecretsProvider { secretPath += path + key; try { const secretResp = await this.#http.get>(secretPath); + this.logger.debug(`Vault provider retrieved secrets from ${secretPath}`); return [ key, kvVersion === '2' @@ -457,6 +462,7 @@ export class VaultProvider extends SecretsProvider { .filter((v): v is [string, IDataObject] => v !== null), ); const name = path.substring(0, path.length - 1); + this.logger.debug(`Vault provider retrieved kv secrets from ${name}`); return [name, data]; } @@ -479,6 +485,7 @@ export class VaultProvider extends SecretsProvider { ).filter((v): v is [string, IDataObject] => v !== null), ); this.cachedSecrets = secrets; + this.logger.debug('Vault provider secrets updated'); } async test(): Promise<[boolean] | [boolean, string]> { diff --git a/packages/cli/test/integration/external-secrets/external-secrets.api.test.ts b/packages/cli/test/integration/external-secrets/external-secrets.api.test.ts index 3418576be1..c36340108e 100644 --- a/packages/cli/test/integration/external-secrets/external-secrets.api.test.ts +++ b/packages/cli/test/integration/external-secrets/external-secrets.api.test.ts @@ -18,7 +18,7 @@ import { MockProviders, TestFailProvider, } from '../../shared/external-secrets/utils'; -import { mockInstance } from '../../shared/mocking'; +import { mockInstance, mockLogger } from '../../shared/mocking'; import { createOwner, createUser } from '../shared/db/users'; import type { SuperAgentTest } from '../shared/types'; import { setupTestServer } from '../shared/utils'; @@ -52,12 +52,14 @@ async function getExternalSecretsSettings(): Promise(); +const logger = mockLogger(); + const resetManager = async () => { Container.get(ExternalSecretsManager).shutdown(); Container.set( ExternalSecretsManager, new ExternalSecretsManager( - mock(), + logger, Container.get(SettingsRepository), Container.get(License), mockProvidersInstance, @@ -108,6 +110,18 @@ beforeAll(async () => { const member = await createUser(); authMemberAgent = testServer.authAgentFor(member); config.set('userManagement.isInstanceOwnerSetUp', true); + Container.set( + ExternalSecretsManager, + new ExternalSecretsManager( + logger, + Container.get(SettingsRepository), + Container.get(License), + mockProvidersInstance, + Container.get(Cipher), + eventService, + mock(), + ), + ); }); beforeEach(async () => { From ab86d8fb9f9871bfe05beb6f33e28428519f203c Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Thu, 24 Oct 2024 12:20:46 +0200 Subject: [PATCH 03/13] test(editor): Set 'data-test-id' in design-system unit tests to match with editor-ui and cypress (no-changelog) (#11381) --- packages/design-system/src/__tests__/setup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/design-system/src/__tests__/setup.ts b/packages/design-system/src/__tests__/setup.ts index 6eb1c426fc..981c9d5a60 100644 --- a/packages/design-system/src/__tests__/setup.ts +++ b/packages/design-system/src/__tests__/setup.ts @@ -1,8 +1,11 @@ import '@testing-library/jest-dom'; +import { configure } from '@testing-library/vue'; import { config } from '@vue/test-utils'; import { N8nPlugin } from 'n8n-design-system/plugin'; +configure({ testIdAttribute: 'data-test-id' }); + config.global.plugins = [N8nPlugin]; window.ResizeObserver = From 247bcc16c8344e9734bf00e198c11cfa3fd2479a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:29:51 +0200 Subject: [PATCH 04/13] :rocket: Release 1.65.0 (#11386) Co-authored-by: cstuncsik <5410822+cstuncsik@users.noreply.github.com> --- CHANGELOG.md | 54 +++++++++++++++++++ package.json | 2 +- packages/@n8n/benchmark/package.json | 2 +- packages/@n8n/config/package.json | 2 +- packages/@n8n/json-schema-to-zod/package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 2 +- packages/@n8n/permissions/package.json | 2 +- packages/@n8n/task-runner/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/design-system/package.json | 2 +- packages/editor-ui/package.json | 2 +- packages/node-dev/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 15 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1558420d0..baa7b95b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,57 @@ +# [1.65.0](https://github.com/n8n-io/n8n/compare/n8n@1.64.0...n8n@1.65.0) (2024-10-24) + + +### Bug Fixes + +* **AI Agent Node:** Preserve `intermediateSteps` when using output parser with non-tool agent ([#11363](https://github.com/n8n-io/n8n/issues/11363)) ([e61a853](https://github.com/n8n-io/n8n/commit/e61a8535aa39653b9a87575ea911a65318282167)) +* **API:** `PUT /credentials/:id` should move the specified credential, not the first one in the database ([#11365](https://github.com/n8n-io/n8n/issues/11365)) ([e6b2f8e](https://github.com/n8n-io/n8n/commit/e6b2f8e7e6ebbb6e3776a976297d519e99ac6c64)) +* **API:** Correct credential schema for response in `POST /credentials` ([#11340](https://github.com/n8n-io/n8n/issues/11340)) ([f495875](https://github.com/n8n-io/n8n/commit/f4958756b4976e0b608b9155dab84564f7e8804e)) +* **core:** Account for waiting jobs during shutdown ([#11338](https://github.com/n8n-io/n8n/issues/11338)) ([c863abd](https://github.com/n8n-io/n8n/commit/c863abd08300b53ea898fc4d06aae97dec7afa9b)) +* **core:** Add missing primary key to execution annotation tags table ([#11168](https://github.com/n8n-io/n8n/issues/11168)) ([b4b543d](https://github.com/n8n-io/n8n/commit/b4b543d41daa07753eca24ab93bf7445f672361d)) +* **core:** Change dedupe value column type from varchar(255) to text ([#11357](https://github.com/n8n-io/n8n/issues/11357)) ([7a71cff](https://github.com/n8n-io/n8n/commit/7a71cff4d75fe4e7282a398b4843428e0161ba8c)) +* **core:** Do not debounce webhooks, triggers and pollers activation ([#11306](https://github.com/n8n-io/n8n/issues/11306)) ([64bddf8](https://github.com/n8n-io/n8n/commit/64bddf86536ddd688638a643d24f80c947a12f31)) +* **core:** Enforce nodejs version consistently ([#11323](https://github.com/n8n-io/n8n/issues/11323)) ([0fa2e8c](https://github.com/n8n-io/n8n/commit/0fa2e8ca85005362d9043d82469f3c3525f4c4ef)) +* **core:** Fix memory issue with empty model response ([#11300](https://github.com/n8n-io/n8n/issues/11300)) ([216b119](https://github.com/n8n-io/n8n/commit/216b119350949de70f15cf2d61f474770803ad7a)) +* **core:** Fix race condition when resolving post-execute promise ([#11360](https://github.com/n8n-io/n8n/issues/11360)) ([4f1816e](https://github.com/n8n-io/n8n/commit/4f1816e03db00219bc2e723e3048848aef7f8fe1)) +* **core:** Sanitise IdP provided information in SAML test pages ([#11171](https://github.com/n8n-io/n8n/issues/11171)) ([74fc388](https://github.com/n8n-io/n8n/commit/74fc3889b946e8f224e65ef8d3d44125404aa4fc)) +* Don't show pin button in input panel when there's binary data ([#11267](https://github.com/n8n-io/n8n/issues/11267)) ([c0b5b92](https://github.com/n8n-io/n8n/commit/c0b5b92f62a2d7ba60492eb27daced268b654fe9)) +* **editor:** Add Personal project to main navigation ([#11161](https://github.com/n8n-io/n8n/issues/11161)) ([1f441f9](https://github.com/n8n-io/n8n/commit/1f441f97528f58e905eaf8930577bbcd08debf06)) +* **editor:** Fix Cannot read properties of undefined (reading 'finished') ([#11367](https://github.com/n8n-io/n8n/issues/11367)) ([475d72e](https://github.com/n8n-io/n8n/commit/475d72e0bc9e13c6dc56129902f6f89c67547f78)) +* **editor:** Fix delete all existing executions ([#11352](https://github.com/n8n-io/n8n/issues/11352)) ([3ec103f](https://github.com/n8n-io/n8n/commit/3ec103f8baaa89e579844947d945f00bec9e498e)) +* **editor:** Fix pin data button disappearing after reload ([#11198](https://github.com/n8n-io/n8n/issues/11198)) ([3b2f63e](https://github.com/n8n-io/n8n/commit/3b2f63e248cd0cba04087e2f40e13d670073707d)) +* **editor:** Fix RunData non-binary pagination when binary data is present ([#11309](https://github.com/n8n-io/n8n/issues/11309)) ([901888d](https://github.com/n8n-io/n8n/commit/901888d5b1027098653540c72f787f176941f35a)) +* **editor:** Fix sorting problem in older browsers that don't support `toSorted` ([#11204](https://github.com/n8n-io/n8n/issues/11204)) ([c728a2f](https://github.com/n8n-io/n8n/commit/c728a2ffe01f510a237979a54897c4680a407800)) +* **editor:** Follow-up fixes to projects side menu ([#11327](https://github.com/n8n-io/n8n/issues/11327)) ([4dde772](https://github.com/n8n-io/n8n/commit/4dde772814c55e66efcc9b369ae443328af21b14)) +* **editor:** Keep always focus on the first item on the node's search panel ([#11193](https://github.com/n8n-io/n8n/issues/11193)) ([c57cac9](https://github.com/n8n-io/n8n/commit/c57cac9e4d447c3a4240a565f9f2de8aa3b7c513)) +* **editor:** Open Community+ enrollment modal only for the instance owner ([#11292](https://github.com/n8n-io/n8n/issues/11292)) ([76724c3](https://github.com/n8n-io/n8n/commit/76724c3be6e001792433045c2b2aac0ef16d4b8a)) +* **editor:** Record sessionStarted telemetry event in Setting Store ([#11334](https://github.com/n8n-io/n8n/issues/11334)) ([1b734dd](https://github.com/n8n-io/n8n/commit/1b734dd9f42885594ce02400cfb395a4f5e7e088)) +* Ensure NDV params don't get cut off early and scrolled to the top ([#11252](https://github.com/n8n-io/n8n/issues/11252)) ([054fe97](https://github.com/n8n-io/n8n/commit/054fe9745ff6864f9088aa4cd66ed9e7869520d5)) +* **HTTP Request Tool Node:** Fix the undefined response issue when authentication is enabled ([#11343](https://github.com/n8n-io/n8n/issues/11343)) ([094ec68](https://github.com/n8n-io/n8n/commit/094ec68d4c00848013aa4eec4ac5efbd2c92afc5)) +* Include error in the message in JS task runner sandbox ([#11359](https://github.com/n8n-io/n8n/issues/11359)) ([0708b3a](https://github.com/n8n-io/n8n/commit/0708b3a1f8097af829c92fe106ea6ba375d6c500)) +* **Microsoft SQL Node:** Fix execute query to allow for non select query to run ([#11335](https://github.com/n8n-io/n8n/issues/11335)) ([ba158b4](https://github.com/n8n-io/n8n/commit/ba158b4f8533bd3430db8766d4921f75db5c1a11)) +* **OpenAI Chat Model Node, Ollama Chat Model Node:** Change default model to a more up-to-date option ([#11293](https://github.com/n8n-io/n8n/issues/11293)) ([0be04c6](https://github.com/n8n-io/n8n/commit/0be04c6348d8c059a96c3d37a6d6cd587bfb97f3)) +* **Pinecone Vector Store Node:** Prevent populating of vectors after manually stopping the execution ([#11288](https://github.com/n8n-io/n8n/issues/11288)) ([fbae17d](https://github.com/n8n-io/n8n/commit/fbae17d8fb35a5197fa183e3639bb36762dc73d2)) +* **Postgres Node:** Special datetime values cause errors ([#11225](https://github.com/n8n-io/n8n/issues/11225)) ([3c57f46](https://github.com/n8n-io/n8n/commit/3c57f46aaeb968d2974f2dc9790317a6a6fab624)) +* Resend invite operation on users list ([#11351](https://github.com/n8n-io/n8n/issues/11351)) ([e4218de](https://github.com/n8n-io/n8n/commit/e4218debd18812fa3aa508339afd3de03c4d69dc)) +* **SSH Node:** Cleanup temporary binary files as soon as possible ([#11305](https://github.com/n8n-io/n8n/issues/11305)) ([08a7b5b](https://github.com/n8n-io/n8n/commit/08a7b5b7425663ec6593114921c2e22ab37d039e)) + + +### Features + +* Add report bug buttons ([#11304](https://github.com/n8n-io/n8n/issues/11304)) ([296f68f](https://github.com/n8n-io/n8n/commit/296f68f041b93fd32ac7be2b53c2b41d58c2998a)) +* **AI Agent Node:** Make tools optional when using OpenAI model with Tools agent ([#11212](https://github.com/n8n-io/n8n/issues/11212)) ([fed7c3e](https://github.com/n8n-io/n8n/commit/fed7c3ec1fb0553adaa9a933f91aabfd54fe83a3)) +* **core:** introduce JWT API keys for the public API ([#11005](https://github.com/n8n-io/n8n/issues/11005)) ([679fa4a](https://github.com/n8n-io/n8n/commit/679fa4a10a85fc96e12ca66fe12cdb32368bc12b)) +* **core:** Enforce config file permissions on startup ([#11328](https://github.com/n8n-io/n8n/issues/11328)) ([c078a51](https://github.com/n8n-io/n8n/commit/c078a516bec857831cc904ef807d0791b889f3a2)) +* **core:** Handle cycles in workflows when partially executing them ([#11187](https://github.com/n8n-io/n8n/issues/11187)) ([321d6de](https://github.com/n8n-io/n8n/commit/321d6deef18806d88d97afef2f2c6f29e739ccb4)) +* **editor:** Separate node output execution tooltip from status icon ([#11196](https://github.com/n8n-io/n8n/issues/11196)) ([cd15e95](https://github.com/n8n-io/n8n/commit/cd15e959c7af82a7d8c682e94add2b2640624a70)) +* **GitHub Node:** Add workflow resource operations ([#10744](https://github.com/n8n-io/n8n/issues/10744)) ([d309112](https://github.com/n8n-io/n8n/commit/d3091126472faa2c8f270650e54027d19dc56bb6)) +* **n8n Form Page Node:** New node ([#10390](https://github.com/n8n-io/n8n/issues/10390)) ([643d66c](https://github.com/n8n-io/n8n/commit/643d66c0ae084a0d93dac652703adc0a32cab8de)) +* **n8n Google My Business Node:** New node ([#10504](https://github.com/n8n-io/n8n/issues/10504)) ([bf28fbe](https://github.com/n8n-io/n8n/commit/bf28fbefe5e8ba648cba1555a2d396b75ee32bbb)) +* Run `mfa.beforeSetup` hook before enabling MFA ([#11116](https://github.com/n8n-io/n8n/issues/11116)) ([25c1c32](https://github.com/n8n-io/n8n/commit/25c1c3218cf1075ca3abd961236f3b2fbd9d6ba9)) +* **Structured Output Parser Node:** Refactor Output Parsers and Improve Error Handling ([#11148](https://github.com/n8n-io/n8n/issues/11148)) ([45274f2](https://github.com/n8n-io/n8n/commit/45274f2e7f081e194e330e1c9e6a5c26fca0b141)) + + + # [1.64.0](https://github.com/n8n-io/n8n/compare/n8n@1.63.0...n8n@1.64.0) (2024-10-16) diff --git a/package.json b/package.json index b75eb0aab1..09c576a8c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.64.0", + "version": "1.65.0", "private": true, "engines": { "node": ">=20.15", diff --git a/packages/@n8n/benchmark/package.json b/packages/@n8n/benchmark/package.json index 40a77afac5..abb8514c3d 100644 --- a/packages/@n8n/benchmark/package.json +++ b/packages/@n8n/benchmark/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-benchmark", - "version": "1.7.0", + "version": "1.8.0", "description": "Cli for running benchmark tests for n8n", "main": "dist/index", "scripts": { diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index 627e9b7ef3..ad583c1108 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/config", - "version": "1.14.0", + "version": "1.15.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/json-schema-to-zod/package.json b/packages/@n8n/json-schema-to-zod/package.json index 859d62226f..e1ae6beaa9 100644 --- a/packages/@n8n/json-schema-to-zod/package.json +++ b/packages/@n8n/json-schema-to-zod/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/json-schema-to-zod", - "version": "1.0.0", + "version": "1.1.0", "description": "Converts JSON schema objects into Zod schemas", "types": "./dist/types/index.d.ts", "main": "./dist/cjs/index.js", diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 0a3dab15cb..3672f73464 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "1.64.0", + "version": "1.65.0", "description": "", "main": "index.js", "scripts": { diff --git a/packages/@n8n/permissions/package.json b/packages/@n8n/permissions/package.json index 0dbc5ee515..d92c2c20ac 100644 --- a/packages/@n8n/permissions/package.json +++ b/packages/@n8n/permissions/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/permissions", - "version": "0.14.0", + "version": "0.15.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/task-runner/package.json b/packages/@n8n/task-runner/package.json index f88fbd7412..d889bde6f3 100644 --- a/packages/@n8n/task-runner/package.json +++ b/packages/@n8n/task-runner/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/task-runner", - "version": "1.2.0", + "version": "1.3.0", "scripts": { "clean": "rimraf dist .turbo", "start": "node dist/start.js", diff --git a/packages/cli/package.json b/packages/cli/package.json index dd2f8e6499..2d95aa1cfa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.64.0", + "version": "1.65.0", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/core/package.json b/packages/core/package.json index 8d6088ccba..0e010052b2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "1.64.0", + "version": "1.65.0", "description": "Core functionality of n8n", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 6b59977d56..8aec0ab60f 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "1.54.0", + "version": "1.55.0", "main": "src/main.ts", "import": "src/main.ts", "scripts": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 69d52a8138..5a4280252e 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.64.0", + "version": "1.65.0", "description": "Workflow Editor UI for n8n", "main": "index.js", "scripts": { diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index a4099626b9..8455dfbc43 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "1.64.0", + "version": "1.65.0", "description": "CLI to simplify n8n credentials/node development", "main": "dist/src/index", "types": "dist/src/index.d.ts", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 4dbc283fb2..f81ce3b248 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "1.64.0", + "version": "1.65.0", "description": "Base nodes of n8n", "main": "index.js", "scripts": { diff --git a/packages/workflow/package.json b/packages/workflow/package.json index d3db16a7d3..b2bd21f35c 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "1.63.0", + "version": "1.64.0", "description": "Workflow base code of n8n", "main": "dist/index.js", "module": "src/index.ts", From f4ea943c9cb2321e41705de6c5c27535a0f5eae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 24 Oct 2024 12:53:34 +0200 Subject: [PATCH 05/13] fix(core): Ensure `LoggerProxy` is not scoped (#11379) --- .../logging/__tests__/logger.service.test.ts | 32 +++++++++++++++++++ packages/cli/src/logging/logger.service.ts | 5 +-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/logging/__tests__/logger.service.test.ts b/packages/cli/src/logging/__tests__/logger.service.test.ts index d01a709639..2ffbf2120e 100644 --- a/packages/cli/src/logging/__tests__/logger.service.test.ts +++ b/packages/cli/src/logging/__tests__/logger.service.test.ts @@ -1,10 +1,42 @@ +jest.mock('n8n-workflow', () => ({ + ...jest.requireActual('n8n-workflow'), + LoggerProxy: { init: jest.fn() }, +})); + import type { GlobalConfig } from '@n8n/config'; import { mock } from 'jest-mock-extended'; import type { InstanceSettings } from 'n8n-core'; +import { LoggerProxy } from 'n8n-workflow'; import { Logger } from '@/logging/logger.service'; describe('Logger', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('constructor', () => { + const globalConfig = mock({ + logging: { + level: 'info', + outputs: ['console'], + scopes: [], + }, + }); + + test('if root, should initialize `LoggerProxy` with instance', () => { + const logger = new Logger(globalConfig, mock(), { isRoot: true }); + + expect(LoggerProxy.init).toHaveBeenCalledWith(logger); + }); + + test('if scoped, should not initialize `LoggerProxy`', () => { + new Logger(globalConfig, mock(), { isRoot: false }); + + expect(LoggerProxy.init).not.toHaveBeenCalled(); + }); + }); + describe('transports', () => { test('if `console` selected, should set console transport', () => { const globalConfig = mock({ diff --git a/packages/cli/src/logging/logger.service.ts b/packages/cli/src/logging/logger.service.ts index 87e5fbe015..46471c0611 100644 --- a/packages/cli/src/logging/logger.service.ts +++ b/packages/cli/src/logging/logger.service.ts @@ -30,6 +30,7 @@ export class Logger { constructor( private readonly globalConfig: GlobalConfig, private readonly instanceSettings: InstanceSettings, + { isRoot }: { isRoot?: boolean } = { isRoot: true }, ) { this.level = this.globalConfig.logging.level; @@ -51,7 +52,7 @@ export class Logger { this.scopes = new Set(scopes); } - LoggerProxy.init(this); + if (isRoot) LoggerProxy.init(this); } private setInternalLogger(internalLogger: winston.Logger) { @@ -61,7 +62,7 @@ export class Logger { /** Create a logger that injects the given scopes into its log metadata. */ scoped(scopes: LogScope | LogScope[]) { scopes = Array.isArray(scopes) ? scopes : [scopes]; - const scopedLogger = new Logger(this.globalConfig, this.instanceSettings); + const scopedLogger = new Logger(this.globalConfig, this.instanceSettings, { isRoot: false }); const childLogger = this.internalLogger.child({ scopes }); scopedLogger.setInternalLogger(childLogger); From 656439e87138f9f96dea5a683cfdac3f661ffefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 24 Oct 2024 13:22:28 +0200 Subject: [PATCH 06/13] fix(core): Add safeguard for command publishing (#11337) --- .../src/scaling/__tests__/publisher.service.test.ts | 10 ++++++++++ packages/cli/src/scaling/pubsub/publisher.service.ts | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/scaling/__tests__/publisher.service.test.ts b/packages/cli/src/scaling/__tests__/publisher.service.test.ts index f77b6b5d5a..f69ad08cb5 100644 --- a/packages/cli/src/scaling/__tests__/publisher.service.test.ts +++ b/packages/cli/src/scaling/__tests__/publisher.service.test.ts @@ -44,6 +44,16 @@ describe('Publisher', () => { }); describe('publishCommand', () => { + it('should do nothing if not in scaling mode', async () => { + config.set('executions.mode', 'regular'); + const publisher = new Publisher(logger, redisClientService, instanceSettings); + const msg = mock({ command: 'reload-license' }); + + await publisher.publishCommand(msg); + + expect(client.publish).not.toHaveBeenCalled(); + }); + it('should publish command into `n8n.commands` pubsub channel', async () => { const publisher = new Publisher(logger, redisClientService, instanceSettings); const msg = mock({ command: 'reload-license' }); diff --git a/packages/cli/src/scaling/pubsub/publisher.service.ts b/packages/cli/src/scaling/pubsub/publisher.service.ts index cc28c2d339..248a455e3e 100644 --- a/packages/cli/src/scaling/pubsub/publisher.service.ts +++ b/packages/cli/src/scaling/pubsub/publisher.service.ts @@ -23,7 +23,7 @@ export class Publisher { private readonly redisClientService: RedisClientService, private readonly instanceSettings: InstanceSettings, ) { - // @TODO: Once this class is only ever initialized in scaling mode, throw in the next line instead. + // @TODO: Once this class is only ever initialized in scaling mode, assert in the next line. if (config.getEnv('executions.mode') !== 'queue') return; this.logger = this.logger.scoped(['scaling', 'pubsub']); @@ -46,6 +46,9 @@ export class Publisher { /** Publish a command into the `n8n.commands` channel. */ async publishCommand(msg: Omit) { + // @TODO: Once this class is only ever used in scaling mode, remove next line. + if (config.getEnv('executions.mode') !== 'queue') return; + await this.client.publish( 'n8n.commands', JSON.stringify({ From ff14dcb3a1ddaea4eca7c1ecd2e92c0abb0c413c Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Thu, 24 Oct 2024 14:22:15 +0200 Subject: [PATCH 07/13] fix(editor): Hide data mapping tooltip in credential edit modal (#11356) --- .../InlineExpressionTip.vue | 18 +++++++++++------- .../src/components/ParameterInput.vue | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue index 897d8304b6..19258d2df9 100644 --- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue +++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue @@ -1,13 +1,13 @@