From 1c261f85a316a0e769864d82d80cda088ed9d6d1 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Mon, 29 Apr 2024 07:53:42 +0300 Subject: [PATCH 001/743] refactor(editor): Migrate header WorkflowDetails to composition api (no-changelog) (#9186) --- .../N8nActionDropdown/ActionDropdown.vue | 16 +- packages/design-system/src/css/tag.scss | 6 +- .../src/types/action-dropdown.ts | 11 + packages/design-system/src/types/index.ts | 1 + packages/editor-ui/src/__tests__/setup.ts | 25 + .../src/components/MainHeader/MainHeader.vue | 7 +- .../MainHeader/WorkflowDetails.spec.ts | 115 ++ .../components/MainHeader/WorkflowDetails.vue | 1195 ++++++++--------- .../editor-ui/src/components/TagsDropdown.vue | 2 +- .../src/composables/useContextMenu.ts | 9 +- packages/editor-ui/src/n8n-theme.scss | 4 +- 11 files changed, 756 insertions(+), 635 deletions(-) create mode 100644 packages/design-system/src/types/action-dropdown.ts create mode 100644 packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts diff --git a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue index 063b228a35..2ec50e72bc 100644 --- a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue +++ b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue @@ -61,23 +61,13 @@ import { ref, useCssModule, useAttrs, computed } from 'vue'; import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus'; import N8nIcon from '../N8nIcon'; import { N8nKeyboardShortcut } from '../N8nKeyboardShortcut'; -import type { KeyboardShortcut } from '../../types'; +import type { ActionDropdownItem } from '../../types'; import type { IconSize } from '@/types/icon'; -interface IActionDropdownItem { - id: string; - label: string; - icon?: string; - divided?: boolean; - disabled?: boolean; - shortcut?: KeyboardShortcut; - customClass?: string; -} - const TRIGGER = ['click', 'hover'] as const; interface ActionDropdownProps { - items: IActionDropdownItem[]; + items: ActionDropdownItem[]; placement?: Placement; activatorIcon?: string; activatorSize?: IconSize; @@ -99,7 +89,7 @@ const $attrs = useAttrs(); const testIdPrefix = $attrs['data-test-id']; const $style = useCssModule(); -const getItemClasses = (item: IActionDropdownItem): Record => { +const getItemClasses = (item: ActionDropdownItem): Record => { return { [$style.itemContainer]: true, [$style.disabled]: !!item.disabled, diff --git a/packages/design-system/src/css/tag.scss b/packages/design-system/src/css/tag.scss index a1634b5d4a..10b09cf32b 100644 --- a/packages/design-system/src/css/tag.scss +++ b/packages/design-system/src/css/tag.scss @@ -127,6 +127,7 @@ border-radius: var.$tag-border-radius; box-sizing: border-box; white-space: nowrap; + line-height: 1; .el-icon.el-tag__close { border-radius: 50%; @@ -137,9 +138,8 @@ height: 16px; width: 16px; line-height: 16px; - vertical-align: middle; - top: -1px; - right: -5px; + margin-top: 0; + margin-right: 0; &::before { display: block; diff --git a/packages/design-system/src/types/action-dropdown.ts b/packages/design-system/src/types/action-dropdown.ts new file mode 100644 index 0000000000..8caf6560f8 --- /dev/null +++ b/packages/design-system/src/types/action-dropdown.ts @@ -0,0 +1,11 @@ +import type { KeyboardShortcut } from '@/types/keyboardshortcut'; + +export interface ActionDropdownItem { + id: string; + label: string; + icon?: string; + divided?: boolean; + disabled?: boolean; + shortcut?: KeyboardShortcut; + customClass?: string; +} diff --git a/packages/design-system/src/types/index.ts b/packages/design-system/src/types/index.ts index eec00e1154..1336f17b63 100644 --- a/packages/design-system/src/types/index.ts +++ b/packages/design-system/src/types/index.ts @@ -1,3 +1,4 @@ +export * from './action-dropdown'; export * from './button'; export * from './datatable'; export * from './form'; diff --git a/packages/editor-ui/src/__tests__/setup.ts b/packages/editor-ui/src/__tests__/setup.ts index 4c75c226e6..3b9c07462e 100644 --- a/packages/editor-ui/src/__tests__/setup.ts +++ b/packages/editor-ui/src/__tests__/setup.ts @@ -19,3 +19,28 @@ Range.prototype.getClientRects = vi.fn(() => ({ length: 0, [Symbol.iterator]: vi.fn(), })); + +export class IntersectionObserver { + root = null; + rootMargin = ''; + thresholds = []; + + disconnect() { + return null; + } + + observe() { + return null; + } + + takeRecords() { + return []; + } + + unobserve() { + return null; + } +} + +window.IntersectionObserver = IntersectionObserver; +global.IntersectionObserver = IntersectionObserver; diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 668b8b5d2a..53eb1fd634 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -2,7 +2,7 @@
- + { + const actual = await import('vue-router'); + + return { + ...actual, + useRoute: () => ({ + value: { + params: { + id: '1', + }, + }, + }), + }; +}); + +const initialState = { + [STORES.SETTINGS]: { + settings: { + enterprise: { + sharing: true, + }, + }, + areTagsEnabled: true, + }, + [STORES.TAGS]: { + tags: { + 1: { + id: '1', + name: 'tag1', + }, + 2: { + id: '2', + name: 'tag2', + }, + }, + }, +}; + +const renderComponent = createComponentRenderer(WorkflowDetails, { + pinia: createTestingPinia({ initialState }), +}); + +describe('WorkflowDetails', () => { + it('renders workflow name and tags', async () => { + const workflow = { + id: '1', + name: 'Test Workflow', + tags: ['1', '2'], + }; + + const { getByTestId, getByText } = renderComponent({ + props: { + workflow, + readOnly: false, + }, + }); + + const workflowName = getByTestId('workflow-name-input'); + const workflowNameInput = workflowName.querySelector('input'); + + expect(workflowNameInput).toHaveValue('Test Workflow'); + expect(getByText('tag1')).toBeInTheDocument(); + expect(getByText('tag2')).toBeInTheDocument(); + }); + + it('calls save function on save button click', async () => { + const onSaveButtonClick = vi.fn(); + const { getByTestId } = renderComponent({ + props: { + workflow: { + id: '1', + name: 'Test Workflow', + tags: [], + }, + readOnly: false, + }, + global: { + mocks: { + onSaveButtonClick, + }, + }, + }); + + await fireEvent.click(getByTestId('workflow-save-button')); + expect(onSaveButtonClick).toHaveBeenCalled(); + }); + + it('opens share modal on share button click', async () => { + const onShareButtonClick = vi.fn(); + const { getByTestId } = renderComponent({ + props: { + workflow: { + id: '1', + name: 'Test Workflow', + tags: [], + }, + readOnly: false, + }, + global: { + mocks: { + onShareButtonClick, + }, + }, + }); + + await fireEvent.click(getByTestId('workflow-share-button')); + expect(onShareButtonClick).toHaveBeenCalled(); + }); +}); diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index b38f72235c..4dc59ef378 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -1,146 +1,4 @@ - - - + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9afb973f9c..270aa4652f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,6 +252,9 @@ importers: '@langchain/google-genai': specifier: ^0.0.12 version: 0.0.12 + '@langchain/groq': + specifier: ^0.0.8 + version: 0.0.8 '@langchain/mistralai': specifier: 0.0.19 version: 0.0.19 @@ -5199,7 +5202,7 @@ packages: dependencies: lodash.camelcase: 4.3.0 long: 5.2.3 - protobufjs: 7.2.4 + protobufjs: 7.2.6 yargs: 17.7.2 dev: false @@ -6324,6 +6327,20 @@ packages: '@langchain/core': 0.1.61 dev: false + /@langchain/groq@0.0.8: + resolution: {integrity: sha512-xqbe35K+12fiYtC/uqkaTT4AXxqL5uvhCrHzc+nBoFkTwM6YfTFE1ch95RZ5G2JnK1U9pKAre/trUSzlU1/6Kg==} + engines: {node: '>=18'} + dependencies: + '@langchain/core': 0.1.61 + '@langchain/openai': 0.0.28 + groq-sdk: 0.3.2 + zod: 3.22.4 + zod-to-json-schema: 3.22.5(zod@3.22.4) + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@langchain/mistralai@0.0.19: resolution: {integrity: sha512-Uin/jve1NCZLAFa9dpOKzE3Y2+uSnMJQX5ria9vO3lnTGRlvBwcMhyGDoTYdI+gnQgHH4ceBoIBzJDlVG+WVWw==} engines: {node: '>=18'} @@ -16077,6 +16094,23 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /groq-sdk@0.3.2: + resolution: {integrity: sha512-Xp1xOea7nqUcTMndpiA8VkjZ05jM/eUUeCILxhRF+c2etBz/myQwRcUrr5lpWc0euIt96AiBMa9aYa0Iqrh13g==} + dependencies: + '@types/node': 18.16.16 + '@types/node-fetch': 2.6.4 + abort-controller: 3.0.0 + agentkeepalive: 4.2.1 + digest-fetch: 1.3.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + web-streams-polyfill: 3.2.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /gtoken@6.1.2: resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==} engines: {node: '>=12.0.0'} @@ -21886,7 +21920,7 @@ packages: resolution: {integrity: sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==} engines: {node: '>=12.0.0'} dependencies: - protobufjs: 7.2.4 + protobufjs: 7.2.6 dev: false /protobufjs-cli@1.1.1(protobufjs@7.2.4): @@ -21927,6 +21961,24 @@ packages: long: 5.2.3 dev: false + /protobufjs@7.2.6: + resolution: {integrity: sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==} + engines: {node: '>=12.0.0'} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.16.16 + long: 5.2.3 + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} From f6c9493355726ddf516fb54a37adf49a2ce0efd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Apr 2024 11:39:24 +0200 Subject: [PATCH 014/743] fix(core): Prevent node param resolution from failing telemetry graph generation (#9257) --- packages/workflow/src/TelemetryHelpers.ts | 22 ++++++++++++------- .../workflow/test/TelemetryHelpers.test.ts | 14 +++++++++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/workflow/src/TelemetryHelpers.ts b/packages/workflow/src/TelemetryHelpers.ts index f93b7337d4..f2f51e9c7d 100644 --- a/packages/workflow/src/TelemetryHelpers.ts +++ b/packages/workflow/src/TelemetryHelpers.ts @@ -126,14 +126,20 @@ export function generateNodesGraph( return; } - const nodeParameters = - getNodeParameters( - stickyType.description.properties, - stickyNote.parameters, - true, - false, - stickyNote, - ) ?? {}; + let nodeParameters: IDataObject = {}; + + try { + nodeParameters = + getNodeParameters( + stickyType.description.properties, + stickyNote.parameters, + true, + false, + stickyNote, + ) ?? {}; + } catch { + // prevent node param resolution from failing graph generation + } const height: number = typeof nodeParameters.height === 'number' ? nodeParameters.height : 0; const width: number = typeof nodeParameters.width === 'number' ? nodeParameters.width : 0; diff --git a/packages/workflow/test/TelemetryHelpers.test.ts b/packages/workflow/test/TelemetryHelpers.test.ts index 203a217e18..11f741e79a 100644 --- a/packages/workflow/test/TelemetryHelpers.test.ts +++ b/packages/workflow/test/TelemetryHelpers.test.ts @@ -5,8 +5,10 @@ import { getDomainBase, getDomainPath, } from '@/TelemetryHelpers'; -import type { IWorkflowBase } from '@/index'; +import { ApplicationError, STICKY_NODE_TYPE, type IWorkflowBase } from '@/index'; import { nodeTypes } from './ExpressionExtensions/Helpers'; +import { mock } from 'jest-mock-extended'; +import * as nodeHelpers from '@/NodeHelpers'; describe('getDomainBase should return protocol plus domain', () => { test('in valid URLs', () => { @@ -763,6 +765,16 @@ describe('generateNodesGraph', () => { webhookNodeNames: [], }); }); + + test('should not fail on error to resolve a node parameter for sticky node type', () => { + const workflow = mock({ nodes: [{ type: STICKY_NODE_TYPE }] }); + + jest.spyOn(nodeHelpers, 'getNodeParameters').mockImplementationOnce(() => { + throw new ApplicationError('Could not find property option'); + }); + + expect(() => generateNodesGraph(workflow, nodeTypes)).not.toThrow(); + }); }); function validUrls(idMaker: typeof alphanumericId | typeof email, char = CHAR) { From 445c05dca46225e195ab122cf77d6d1088460e20 Mon Sep 17 00:00:00 2001 From: oleg Date: Tue, 30 Apr 2024 12:23:07 +0200 Subject: [PATCH 015/743] fix(LangChain Code Node): Fix resolution of scoped langchain modules (#9258) Signed-off-by: Oleg Ivaniv --- packages/@n8n/nodes-langchain/nodes/code/Code.node.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/code/Code.node.ts b/packages/@n8n/nodes-langchain/nodes/code/Code.node.ts index 30b205e9da..d85b3675c3 100644 --- a/packages/@n8n/nodes-langchain/nodes/code/Code.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/code/Code.node.ts @@ -48,13 +48,14 @@ return [ {json: { output } } ];`; const defaultCodeSupplyData = `const { WikipediaQueryRun } = require('langchain/tools'); return new WikipediaQueryRun();`; +const langchainModules = ['langchain', '@langchain/*']; export const vmResolver = makeResolverFromLegacyOptions({ external: { - modules: external ? ['langchain', ...external.split(',')] : ['langchain'], + modules: external ? [...langchainModules, ...external.split(',')] : [...langchainModules], transitive: false, }, resolve(moduleName, parentDirname) { - if (moduleName.match(/^langchain\//)) { + if (moduleName.match(/^langchain\//) ?? moduleName.match(/^@langchain\//)) { return require.resolve(`@n8n/n8n-nodes-langchain/node_modules/${moduleName}.cjs`, { paths: [parentDirname], }); From 5cb51b79ce7fd7a725ecf58c44371b4bbef780d1 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 30 Apr 2024 11:47:45 +0100 Subject: [PATCH 016/743] fix(Customer.io Node): Update logo (no-changelog) (#9262) --- packages/nodes-base/nodes/CustomerIo/customerio.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/CustomerIo/customerio.svg b/packages/nodes-base/nodes/CustomerIo/customerio.svg index 0ef4bf0101..6230b5741a 100644 --- a/packages/nodes-base/nodes/CustomerIo/customerio.svg +++ b/packages/nodes-base/nodes/CustomerIo/customerio.svg @@ -1 +1 @@ - \ No newline at end of file + From 9f6f8eae6d259b3c8430d50d38ae5335b0d45f0e Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 30 Apr 2024 11:48:01 +0100 Subject: [PATCH 017/743] fix(Baserow Node): Update logo (no-changelog) (#9261) --- packages/nodes-base/nodes/Baserow/baserow.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Baserow/baserow.svg b/packages/nodes-base/nodes/Baserow/baserow.svg index 14051c4065..83210dad5d 100644 --- a/packages/nodes-base/nodes/Baserow/baserow.svg +++ b/packages/nodes-base/nodes/Baserow/baserow.svg @@ -1 +1 @@ -image/svg+xml + + diff --git a/packages/editor-ui/src/components/ImportCurlModal.vue b/packages/editor-ui/src/components/ImportCurlModal.vue index eb0f508497..12e6a32b3e 100644 --- a/packages/editor-ui/src/components/ImportCurlModal.vue +++ b/packages/editor-ui/src/components/ImportCurlModal.vue @@ -1,37 +1,37 @@