From beedfb609ccde2ef202e08566580a2e1a6b6eafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 22 Jun 2023 16:47:28 +0200 Subject: [PATCH] feat(editor): SQL editor overhaul (#6282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Draft setup * ⚡ Implemented expression evaluation in Postgres node, minor SQL editor UI improvements, minor refacring * ⚡ Added initial version of expression preview for SQL editor * ⚡ Linking npm package for codemirror sql grammar instead of a local file * ⚡ Moving expression editor wrapper elements to the component * ⚡ Using expression preview in SQL editor * Use SQL parser skipping whitespace * ✨ Added support for custom skipped segments specification * ✨ Fixing highlight problems with dots and expressions that resolve to zero * 👕 Fixing linting error * ✨ Added current item support * ⚡ Added expression support to more nodes with sql editor * ✨ Added expression support for other nodes * ✨ Implemented different SQL dialect support * 🐛 Fixing hard-coded parameter names for editors * ✨ Fixing preview for nested queries, updating query when input data changes, adding keyboard shortcut to toggle comments * ✨ Adding a custom automcomplete notice for different editors * ⚡ Updating SQL autocomplete notice * ✅ Added unit tests for SQL editor * ⚡ Using latest grammar * 🐛 Fixing code node editor rendering * 💄 SQL preview dropdown matches editor width. Removing unnecessary css * ⚡ Addressing PR review feedback * 👌 Addressing PR review feedback pt2 * 👌 Added path alias for utils in nodes-base package * 👌 Addressing more PR review feedback * ✅ Adding tests for `getResolvables` utility function * ⚡Fixing lodash imports * 👌 Better focus handling, adding more plugins to the editor, other minor imrovements * ⚡ Not showing SQL autocomplete suggestions inside expressions * ⚡ Using npm package for sql grammar * ⚡ Removing autocomplete notice, adding line highlight on syntax error * 👌 Addressing code review feedback --------- Co-authored-by: Milorad Filipovic --- packages/editor-ui/package.json | 2 +- .../src/components/CodeNodeEditor/theme.ts | 5 +- .../components/ExpressionParameterInput.vue | 107 +-------- .../InlineExpressionEditorOutput.vue | 107 ++++++++- .../src/components/ParameterOptions.vue | 2 +- .../src/components/SqlEditor/SqlEditor.vue | 217 ++++++++++++++---- .../components/__tests__/SQLEditor.test.ts | 112 +++++++++ .../editor-ui/src/mixins/expressionManager.ts | 26 ++- packages/editor-ui/src/stores/ndv.store.ts | 11 + .../src/styles/autocomplete-theme.scss | 7 +- packages/nodes-base/jest.config.js | 1 + .../nodes/CompareDatasets/GenericFunctions.ts | 2 +- .../nodes-base/nodes/CrateDb/CrateDb.node.ts | 15 +- .../nodes/EmailSend/v2/send.operation.ts | 2 +- .../Analytics/v1/GoogleAnalyticsV1.node.ts | 2 +- .../BigQuery/v1/GoogleBigQueryV1.node.ts | 2 +- .../database/executeQuery.operation.ts | 10 +- .../v2/actions/database/insert.operation.ts | 2 +- .../nodes/Google/Gmail/v1/GmailV1.node.ts | 2 +- packages/nodes-base/nodes/Html/Html.node.ts | 3 +- packages/nodes-base/nodes/Html/utils.ts | 20 -- .../HttpRequest/V3/HttpRequestV3.node.ts | 2 +- .../nodes-base/nodes/Merge/v1/MergeV1.node.ts | 2 +- .../nodes/Merge/v2/GenericFunctions.ts | 2 +- .../v2/actions/table/addTable.operation.ts | 2 +- .../v2/actions/table/append.operation.ts | 2 +- .../actions/table/convertToRange.operation.ts | 2 +- .../v2/actions/table/deleteTable.operation.ts | 2 +- .../v2/actions/table/getColumns.operation.ts | 2 +- .../v2/actions/table/getRows.operation.ts | 2 +- .../v2/actions/table/lookup.operation.ts | 2 +- .../workbook/addWorksheet.operation.ts | 2 +- .../workbook/deleteWorkbook.operation.ts | 2 +- .../v2/actions/workbook/getAll.operation.ts | 2 +- .../v2/actions/worksheet/append.operation.ts | 2 +- .../v2/actions/worksheet/clear.operation.ts | 2 +- .../worksheet/deleteWorksheet.operation.ts | 2 +- .../v2/actions/worksheet/getAll.operation.ts | 2 +- .../actions/worksheet/readRows.operation.ts | 2 +- .../v2/actions/worksheet/update.operation.ts | 2 +- .../v2/actions/worksheet/upsert.operation.ts | 2 +- .../nodes/Microsoft/Excel/v2/helpers/utils.ts | 2 +- .../nodes/Microsoft/Sql/MicrosoftSql.node.ts | 11 +- .../nodes-base/nodes/MySql/v1/MySqlV1.node.ts | 5 +- .../actions/database/deleteTable.operation.ts | 2 +- .../database/executeQuery.operation.ts | 11 +- .../v2/actions/database/insert.operation.ts | 2 +- .../v2/actions/database/select.operation.ts | 2 +- .../v2/actions/database/update.operation.ts | 2 +- .../v2/actions/database/upsert.operation.ts | 2 +- .../nodes/Postgres/v1/PostgresV1.node.ts | 5 +- .../nodes/Postgres/v1/genericFunctions.ts | 19 +- .../actions/database/deleteTable.operation.ts | 2 +- .../database/executeQuery.operation.ts | 13 +- .../v2/actions/database/insert.operation.ts | 2 +- .../v2/actions/database/select.operation.ts | 2 +- .../v2/actions/database/update.operation.ts | 2 +- .../v2/actions/database/upsert.operation.ts | 2 +- .../nodes-base/nodes/QuestDb/QuestDb.node.ts | 17 +- .../nodes-base/nodes/Slack/V1/SlackV1.node.ts | 2 +- .../nodes/Snowflake/Snowflake.node.ts | 9 +- .../nodes/TimescaleDb/TimescaleDb.node.ts | 15 +- packages/nodes-base/package.json | 2 +- .../nodes-base/test/utils/utilities.test.ts | 38 ++- packages/nodes-base/tsconfig.json | 12 +- packages/nodes-base/utils/utilities.ts | 20 ++ packages/workflow/src/Interfaces.ts | 10 +- pnpm-lock.yaml | 32 +-- 68 files changed, 653 insertions(+), 287 deletions(-) create mode 100644 packages/editor-ui/src/components/__tests__/SQLEditor.test.ts diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 0f9d8a9c25..1312df7888 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -31,7 +31,6 @@ "@codemirror/lang-javascript": "^6.1.2", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-python": "^6.1.2", - "@codemirror/lang-sql": "^6.4.1", "@codemirror/language": "^6.2.1", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.1.4", @@ -46,6 +45,7 @@ "@jsplumb/connector-bezier": "^5.13.2", "@jsplumb/core": "^5.13.2", "@jsplumb/util": "^5.13.2", + "@n8n/codemirror-lang-sql": "^1.0.0", "axios": "^0.21.1", "codemirror-lang-html-n8n": "^1.0.0", "codemirror-lang-n8n-expression": "^0.2.0", diff --git a/packages/editor-ui/src/components/CodeNodeEditor/theme.ts b/packages/editor-ui/src/components/CodeNodeEditor/theme.ts index 166d214be8..f54afaf71f 100644 --- a/packages/editor-ui/src/components/CodeNodeEditor/theme.ts +++ b/packages/editor-ui/src/components/CodeNodeEditor/theme.ts @@ -31,9 +31,10 @@ const cssStyleDeclaration = getComputedStyle(document.documentElement); interface ThemeSettings { isReadOnly?: boolean; + customMaxHeight?: string; } -export const codeNodeEditorTheme = ({ isReadOnly }: ThemeSettings) => [ +export const codeNodeEditorTheme = ({ isReadOnly, customMaxHeight }: ThemeSettings) => [ EditorView.theme({ '&': { 'font-size': BASE_STYLING.fontSize, @@ -79,7 +80,7 @@ export const codeNodeEditorTheme = ({ isReadOnly }: ThemeSettings) => [ }, '.cm-scroller': { overflow: 'auto', - maxHeight: '100%', + maxHeight: customMaxHeight ?? '100%', ...(isReadOnly ? {} : { minHeight: '10em' }), }, '.cm-diagnosticAction': { diff --git a/packages/editor-ui/src/components/ExpressionParameterInput.vue b/packages/editor-ui/src/components/ExpressionParameterInput.vue index c1e56ed90c..c597a1edd5 100644 --- a/packages/editor-ui/src/components/ExpressionParameterInput.vue +++ b/packages/editor-ui/src/components/ExpressionParameterInput.vue @@ -31,36 +31,13 @@ /> -
- - {{ $locale.baseText('parameterInput.resultForItem') }} {{ hoveringItemNumber }} - - - - -
- - {{ $locale.baseText('parameterInput.anythingInside') }} - -
- - {{ $locale.baseText('parameterInput.isJavaScript') }} - - - {{ $locale.baseText('parameterInput.learnMore') }} - -
-
+ @@ -74,7 +51,6 @@ import InlineExpressionEditorInput from '@/components/InlineExpressionEditor/Inl import InlineExpressionEditorOutput from '@/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue'; import ExpressionFunctionIcon from '@/components/ExpressionFunctionIcon.vue'; import { createExpressionTelemetryPayload } from '@/utils/telemetryUtils'; -import { EXPRESSIONS_DOCS_URL } from '@/constants'; import type { Segment } from '@/types/expressions'; import type { TargetItem } from '@/Interface'; @@ -92,7 +68,6 @@ export default defineComponent({ return { isFocused: false, segments: [] as Segment[], - expressionsDocsUrl: EXPRESSIONS_DOCS_URL, }; }, props: { @@ -114,14 +89,10 @@ export default defineComponent({ computed: { ...mapStores(useNDVStore, useWorkflowsStore), hoveringItemNumber(): number { - return (this.hoveringItem?.itemIndex ?? 0) + 1; + return this.ndvStore.hoveringItemNumber; }, hoveringItem(): TargetItem | null { - if (this.ndvStore.isInputParentOfActiveNode) { - return this.ndvStore.hoveringItem; - } - - return null; + return this.ndvStore.getHoveringItem; }, isDragging(): boolean { return this.ndvStore.isDraggableDragging; @@ -241,64 +212,4 @@ export default defineComponent({ border-bottom-right-radius: 0; background-color: white; } - -.hidden { - display: none; -} - -.dropdown { - display: flex; - flex-direction: column; - position: absolute; - z-index: 2; // cover tooltips - background: white; - border: var(--border-base); - border-top: none; - width: 100%; - box-shadow: 0 2px 6px 0 rgba(#441c17, 0.1); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - - .header, - .body, - .footer { - padding: var(--spacing-3xs); - } - - .header { - color: var(--color-text-dark); - font-weight: var(--font-weight-bold); - padding-left: var(--spacing-2xs); - padding-top: var(--spacing-2xs); - } - - .body { - padding-top: 0; - padding-left: var(--spacing-2xs); - color: var(--color-text-dark); - } - - .footer { - border-top: var(--border-base); - padding: var(--spacing-4xs); - padding-left: var(--spacing-2xs); - padding-top: 0; - line-height: var(--font-line-height-regular); - color: var(--color-text-base); - - .expression-syntax-example { - display: inline-block; - font-size: var(--font-size-2xs); - height: var(--font-size-m); - background-color: #f0f0f0; - margin-left: var(--spacing-5xs); - margin-right: var(--spacing-5xs); - } - - .learn-more { - line-height: 1; - white-space: nowrap; - } - } -} diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue index b64a70a2df..472fef3ed2 100644 --- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue +++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue @@ -1,5 +1,30 @@ - + diff --git a/packages/editor-ui/src/components/ParameterOptions.vue b/packages/editor-ui/src/components/ParameterOptions.vue index e4453ccc72..a00bb5c22d 100644 --- a/packages/editor-ui/src/components/ParameterOptions.vue +++ b/packages/editor-ui/src/components/ParameterOptions.vue @@ -107,7 +107,7 @@ export default defineComponent({ return false; } - if (this.parameter.typeOptions?.editor === 'codeNodeEditor') { + if (['codeNodeEditor', 'sqlEditor'].includes(this.parameter.typeOptions?.editor)) { return false; } diff --git a/packages/editor-ui/src/components/SqlEditor/SqlEditor.vue b/packages/editor-ui/src/components/SqlEditor/SqlEditor.vue index adac053295..09cee368d0 100644 --- a/packages/editor-ui/src/components/SqlEditor/SqlEditor.vue +++ b/packages/editor-ui/src/components/SqlEditor/SqlEditor.vue @@ -1,17 +1,23 @@ + + diff --git a/packages/editor-ui/src/components/__tests__/SQLEditor.test.ts b/packages/editor-ui/src/components/__tests__/SQLEditor.test.ts new file mode 100644 index 0000000000..af33117c63 --- /dev/null +++ b/packages/editor-ui/src/components/__tests__/SQLEditor.test.ts @@ -0,0 +1,112 @@ +import { render } from '@testing-library/vue'; +import { PiniaVuePlugin } from 'pinia'; +import { SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils'; +import { STORES } from '@/constants'; +import { createTestingPinia } from '@pinia/testing'; + +import SqlEditor from '@/components/SqlEditor/SqlEditor.vue'; +import { expressionManager } from '@/mixins/expressionManager'; +import type { TargetItem } from '@/Interface'; + +const EXPRESSION_OUTPUT_TEST_ID = 'inline-expression-editor-output'; + +const RESOLVABLES: { [key: string]: string | number | boolean } = { + '{{ $json.schema }}': 'public', + '{{ $json.table }}': 'users', + '{{ $json.id }}': 'id', + '{{ $json.limit - 10 }}': 0, + '{{ $json.active }}': false, +}; + +const DEFAULT_SETUP = { + pinia: createTestingPinia({ + initialState: { + [STORES.SETTINGS]: { + settings: SETTINGS_STORE_DEFAULT_STATE.settings, + }, + }, + }), + props: { + dialect: 'PostgreSQL', + isReadOnly: false, + }, +}; + +const renderComponent = (renderOptions: Parameters[1] = {}) => + render(SqlEditor, { ...DEFAULT_SETUP, ...renderOptions }, (vue) => { + vue.use(PiniaVuePlugin); + }); + +describe('SQL Editor Preview Tests', () => { + beforeEach(() => { + vi.spyOn(expressionManager.methods, 'resolve').mockImplementation( + (resolvable: string, _targetItem?: TargetItem) => { + return { resolved: RESOLVABLES[resolvable] }; + }, + ); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('renders basic query', async () => { + const { getByTestId } = renderComponent({ + props: { + query: 'SELECT * FROM users', + }, + }); + await waitAllPromises(); + expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM users'); + }); + + it('renders basic query with expression', async () => { + const { getByTestId } = renderComponent({ + props: { + query: 'SELECT * FROM {{ $json.table }}', + }, + }); + await waitAllPromises(); + expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM users'); + }); + + it('renders resolved expressions with dot between resolvables', async () => { + const { getByTestId } = renderComponent({ + props: { + query: 'SELECT * FROM {{ $json.schema }}.{{ $json.table }}', + }, + }); + await waitAllPromises(); + expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent('SELECT * FROM public.users'); + }); + + it('renders resolved expressions which resolve to 0', async () => { + const { getByTestId } = renderComponent({ + props: { + query: + 'SELECT * FROM {{ $json.schema }}.{{ $json.table }} WHERE {{ $json.id }} > {{ $json.limit - 10 }}', + }, + }); + await waitAllPromises(); + expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent( + 'SELECT * FROM public.users WHERE id > 0', + ); + }); + + it('keeps query formatting in rendered output', async () => { + const { getByTestId } = renderComponent({ + props: { + query: + 'SELECT * FROM {{ $json.schema }}.{{ $json.table }}\n WHERE id > {{ $json.limit - 10 }}\n AND active = {{ $json.active }};', + }, + }); + await waitAllPromises(); + expect(getByTestId(EXPRESSION_OUTPUT_TEST_ID)).toHaveTextContent( + 'SELECT * FROM public.users WHERE id > 0 AND active = false;', + ); + // Output should have the same number of lines as the input + expect(getByTestId('sql-editor-container').getElementsByClassName('cm-line').length).toEqual( + getByTestId(EXPRESSION_OUTPUT_TEST_ID).getElementsByClassName('cm-line').length, + ); + }); +}); diff --git a/packages/editor-ui/src/mixins/expressionManager.ts b/packages/editor-ui/src/mixins/expressionManager.ts index e4a8128a83..f0cf517e50 100644 --- a/packages/editor-ui/src/mixins/expressionManager.ts +++ b/packages/editor-ui/src/mixins/expressionManager.ts @@ -23,6 +23,7 @@ export const expressionManager = defineComponent({ data() { return { editor: {} as EditorView, + skipSegments: [] as string[], }; }, watch: { @@ -71,6 +72,8 @@ export const expressionManager = defineComponent({ }, segments(): Segment[] { + if (!this.editor?.state) return []; + const rawSegments: RawSegment[] = []; const fullTree = ensureSyntaxTree( @@ -83,14 +86,18 @@ export const expressionManager = defineComponent({ throw new Error(`Failed to parse expression: ${this.editor.state.doc.toString()}`); } + const skipSegments = ['Program', 'Script', 'Document', ...this.skipSegments]; + fullTree.cursor().iterate((node) => { - if (node.type.name === 'Program') return; + const text = this.editor.state.sliceDoc(node.from, node.to); + + if (skipSegments.includes(node.type.name)) return; rawSegments.push({ from: node.from, to: node.to, - text: this.editor.state.sliceDoc(node.from, node.to), - token: node.type.name, + text, + token: node.type.name === 'Resolvable' ? 'Resolvable' : 'Plaintext', }); }); @@ -100,7 +107,18 @@ export const expressionManager = defineComponent({ if (token === 'Resolvable') { const { resolved, error, fullError } = this.resolve(text, this.hoveringItem); - acc.push({ kind: 'resolvable', from, to, resolvable: text, resolved, error, fullError }); + acc.push({ + kind: 'resolvable', + from, + to, + resolvable: text, + // TODO: + // For some reason, expressions that resolve to a number 0 are breaking preview in the SQL editor + // This fixes that but as as TODO we should figure out why this is happening + resolved: String(resolved), + error, + fullError, + }); return acc; } diff --git a/packages/editor-ui/src/stores/ndv.store.ts b/packages/editor-ui/src/stores/ndv.store.ts index 3778255f41..7b884219fd 100644 --- a/packages/editor-ui/src/stores/ndv.store.ts +++ b/packages/editor-ui/src/stores/ndv.store.ts @@ -4,6 +4,7 @@ import type { IRunDataDisplayMode, NDVState, NodePanelType, + TargetItem, XYPosition, } from '@/Interface'; import type { INodeIssues, IRunData } from 'n8n-workflow'; @@ -125,6 +126,16 @@ export const useNDVStore = defineStore(STORES.NDV, { const parentNodes = workflow.getParentNodes(this.activeNode.name, 'main', 1); return parentNodes.includes(inputNodeName); }, + hoveringItemNumber(): number { + return (this.hoveringItem?.itemIndex ?? 0) + 1; + }, + getHoveringItem(): TargetItem | null { + if (this.isInputParentOfActiveNode) { + return this.hoveringItem; + } + + return null; + }, }, actions: { setInputNodeName(nodeName: string | undefined): void { diff --git a/packages/editor-ui/src/styles/autocomplete-theme.scss b/packages/editor-ui/src/styles/autocomplete-theme.scss index 5cdf4f7442..5c406de1f2 100644 --- a/packages/editor-ui/src/styles/autocomplete-theme.scss +++ b/packages/editor-ui/src/styles/autocomplete-theme.scss @@ -1,10 +1,13 @@ .cm-tooltip-autocomplete:after { - display: block; - content: 'n8n supports all JavaScript functions, including those not listed.'; padding: var(--spacing-2xs) var(--spacing-s); border-top: 1px solid var(--color-foreground-dark); } +.code-node-editor .cm-tooltip-autocomplete:after { + display: block; + content: 'n8n supports all JavaScript functions, including those not listed.'; +} + // Custom autocomplete item type icons // 1. Native and n8n extension functions: .cm-completionIcon-extension-function, .cm-completionIcon-native-function { diff --git a/packages/nodes-base/jest.config.js b/packages/nodes-base/jest.config.js index 5fcf33c335..d21cd780b8 100644 --- a/packages/nodes-base/jest.config.js +++ b/packages/nodes-base/jest.config.js @@ -4,5 +4,6 @@ module.exports = { collectCoverageFrom: ['credentials/**/*.ts', 'nodes/**/*.ts', 'utils/**/*.ts'], moduleNameMapper: { '^@test/(.*)$': '/test/$1', + '^@utils/(.*)$': '/utils/$1', }, }; diff --git a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts index d3c66edba8..c8e4709c46 100644 --- a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts @@ -9,7 +9,7 @@ import unset from 'lodash/unset'; import { cloneDeep } from 'lodash'; import set from 'lodash/set'; import union from 'lodash/union'; -import { fuzzyCompare } from '../../utils/utilities'; +import { fuzzyCompare } from '@utils/utilities'; type PairToMatch = { field1: string; diff --git a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts index 6bc61ee8ac..689881910e 100644 --- a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts +++ b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts @@ -11,7 +11,7 @@ import { getItemCopy, getItemsCopy, pgInsert, - pgQuery, + pgQueryV2, pgUpdate, } from '../Postgres/v1/genericFunctions'; @@ -73,9 +73,10 @@ export class CrateDb implements INodeType { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'postgres', + sqlDialect: 'PostgreSQL', }, displayOptions: { show: { @@ -283,13 +284,9 @@ export class CrateDb implements INodeType { // executeQuery // ---------------------------------- - const queryResult = await pgQuery( - this.getNodeParameter, - pgp, - db, - items, - this.continueOnFail(), - ); + const queryResult = await pgQueryV2.call(this, pgp, db, items, this.continueOnFail(), { + resolveExpression: true, + }); returnItems = this.helpers.returnJsonArray(queryResult); } else if (operation === 'insert') { diff --git a/packages/nodes-base/nodes/EmailSend/v2/send.operation.ts b/packages/nodes-base/nodes/EmailSend/v2/send.operation.ts index 0c2dda97ed..3a80e306c6 100644 --- a/packages/nodes-base/nodes/EmailSend/v2/send.operation.ts +++ b/packages/nodes-base/nodes/EmailSend/v2/send.operation.ts @@ -10,7 +10,7 @@ import { NodeApiError } from 'n8n-workflow'; import { createTransport } from 'nodemailer'; import type SMTPTransport from 'nodemailer/lib/smtp-transport'; -import { updateDisplayOptions } from '../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; const properties: INodeProperties[] = [ // TODO: Add choice for text as text or html (maybe also from name) diff --git a/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts b/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts index e2977a6d49..04d668464b 100644 --- a/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts +++ b/packages/nodes-base/nodes/Google/Analytics/v1/GoogleAnalyticsV1.node.ts @@ -16,7 +16,7 @@ import { googleApiRequest, googleApiRequestAllItems, merge, simplify } from './G import moment from 'moment-timezone'; import type { IData } from './Interfaces'; -import { oldVersionNotice } from '../../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; const versionDescription: INodeTypeDescription = { displayName: 'Google Analytics', diff --git a/packages/nodes-base/nodes/Google/BigQuery/v1/GoogleBigQueryV1.node.ts b/packages/nodes-base/nodes/Google/BigQuery/v1/GoogleBigQueryV1.node.ts index 394e32c6ad..748aaad449 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/v1/GoogleBigQueryV1.node.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/v1/GoogleBigQueryV1.node.ts @@ -20,7 +20,7 @@ import { recordFields, recordOperations } from './RecordDescription'; import { v4 as uuid } from 'uuid'; -import { oldVersionNotice } from '../../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; const versionDescription: INodeTypeDescription = { displayName: 'Google BigQuery', diff --git a/packages/nodes-base/nodes/Google/BigQuery/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/Google/BigQuery/v2/actions/database/executeQuery.operation.ts index 0e78130098..7a73c36ab8 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/v2/actions/database/executeQuery.operation.ts @@ -3,7 +3,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError, sleep } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { getResolvables, updateDisplayOptions } from '@utils/utilities'; import type { JobInsertResponse } from '../../helpers/interfaces'; import { prepareOutput } from '../../helpers/utils'; @@ -14,6 +14,7 @@ const properties: INodeProperties[] = [ displayName: 'SQL Query', name: 'sqlQuery', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', }, @@ -31,6 +32,7 @@ const properties: INodeProperties[] = [ displayName: 'SQL Query', name: 'sqlQuery', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', }, @@ -160,12 +162,16 @@ export async function execute(this: IExecuteFunctions): Promise string | undefined; diff --git a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts index 57483e5829..7828836563 100644 --- a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts +++ b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts @@ -24,7 +24,7 @@ import { replaceNullValues, sanitizeUiMessage, } from '../GenericFunctions'; -import { keysToLowercase } from '../../../utils/utilities'; +import { keysToLowercase } from '@utils/utilities'; function toText(data: T) { if (typeof data === 'object' && data !== null) { diff --git a/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts b/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts index 50114a9041..d0f10e29b2 100644 --- a/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts +++ b/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts @@ -12,7 +12,7 @@ import type { } from 'n8n-workflow'; import { deepCopy } from 'n8n-workflow'; -import { oldVersionNotice } from '../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; const versionDescription: INodeTypeDescription = { displayName: 'Merge', diff --git a/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts b/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts index 88cc5f24a4..19f2f8bffb 100644 --- a/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts @@ -11,7 +11,7 @@ import assignWith from 'lodash/assignWith'; import get from 'lodash/get'; import merge from 'lodash/merge'; import mergeWith from 'lodash/mergeWith'; -import { fuzzyCompare } from '../../../utils/utilities'; +import { fuzzyCompare } from '@utils/utilities'; type PairToMatch = { field1: string; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/addTable.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/addTable.operation.ts index da1d445547..6e575516c2 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/addTable.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/addTable.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/append.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/append.operation.ts index e97b2d18e4..3d335e88a5 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/append.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/append.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { processJsonInput, updateDisplayOptions } from '../../../../../../utils/utilities'; +import { processJsonInput, updateDisplayOptions } from '@utils/utilities'; import type { ExcelResponse } from '../../helpers/interfaces'; import { prepareOutput } from '../../helpers/utils'; import { microsoftApiRequest } from '../../transport'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/convertToRange.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/convertToRange.operation.ts index 281b15461d..f5a9337710 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/convertToRange.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/convertToRange.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { tableRLC, workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/deleteTable.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/deleteTable.operation.ts index 171c80044c..367b5ed3bf 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/deleteTable.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/deleteTable.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { tableRLC, workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getColumns.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getColumns.operation.ts index 18260d84d2..ea49a70356 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getColumns.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getColumns.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest, microsoftApiRequestAllItemsSkip } from '../../transport'; import { tableRLC, workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getRows.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getRows.operation.ts index bc0c56f940..41a48f0fd2 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getRows.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/getRows.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest, microsoftApiRequestAllItemsSkip } from '../../transport'; import { tableRLC, workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/lookup.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/lookup.operation.ts index 6229930ad8..cbfa515d90 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/lookup.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/table/lookup.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties, JsonObject } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequestAllItemsSkip } from '../../transport'; import { tableRLC, workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/addWorksheet.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/addWorksheet.operation.ts index 856a909f28..ec8701723c 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/addWorksheet.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/addWorksheet.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { workbookRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/deleteWorkbook.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/deleteWorkbook.operation.ts index b97acce63a..15dc65335b 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/deleteWorkbook.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/deleteWorkbook.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { workbookRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/getAll.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/getAll.operation.ts index bcff50920a..0c24643f5f 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/getAll.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/workbook/getAll.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport'; const properties: INodeProperties[] = [ diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/append.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/append.operation.ts index 7cb57d6782..b572b69a13 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/append.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/append.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { processJsonInput, updateDisplayOptions } from '../../../../../../utils/utilities'; +import { processJsonInput, updateDisplayOptions } from '@utils/utilities'; import type { ExcelResponse } from '../../helpers/interfaces'; import { prepareOutput } from '../../helpers/utils'; import { microsoftApiRequest } from '../../transport'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/clear.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/clear.operation.ts index 4e9b5ab8cb..36205d4a60 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/clear.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/clear.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/deleteWorksheet.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/deleteWorksheet.operation.ts index e9305e219f..4d9bc49f06 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/deleteWorksheet.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/deleteWorksheet.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest } from '../../transport'; import { workbookRLC, worksheetRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/getAll.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/getAll.operation.ts index 16cc37fab0..e69d59767c 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/getAll.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/getAll.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport'; import { workbookRLC } from '../common.descriptions'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/readRows.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/readRows.operation.ts index eeb791c1ae..7b8e095de1 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/readRows.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/readRows.operation.ts @@ -1,6 +1,6 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { ExcelResponse } from '../../helpers/interfaces'; import { prepareOutput } from '../../helpers/utils'; import { microsoftApiRequest } from '../../transport'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/update.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/update.operation.ts index 58805994c6..8ac73272eb 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/update.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/update.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { processJsonInput, updateDisplayOptions } from '../../../../../../utils/utilities'; +import { processJsonInput, updateDisplayOptions } from '@utils/utilities'; import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces'; import { prepareOutput, updateByAutoMaping, updateByDefinedValues } from '../../helpers/utils'; import { microsoftApiRequest } from '../../transport'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/upsert.operation.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/upsert.operation.ts index fa635aac83..160ef5ba53 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/upsert.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/worksheet/upsert.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { processJsonInput, updateDisplayOptions } from '../../../../../../utils/utilities'; +import { processJsonInput, updateDisplayOptions } from '@utils/utilities'; import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces'; import { prepareOutput, updateByAutoMaping, updateByDefinedValues } from '../../helpers/utils'; import { microsoftApiRequest } from '../../transport'; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/helpers/utils.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/helpers/utils.ts index 650ac8ad7f..1f71147c3d 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/helpers/utils.ts @@ -3,7 +3,7 @@ import type { IDataObject, INode, INodeExecutionData } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; import type { ExcelResponse, SheetData, UpdateSummary } from './interfaces'; import { constructExecutionMetaData } from 'n8n-core'; -import { wrapData } from '../../../../../utils/utilities'; +import { wrapData } from '@utils/utilities'; type PrepareOutputConfig = { rawData: boolean; diff --git a/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts b/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts index 14a39fdddb..9a0a353960 100644 --- a/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts @@ -11,7 +11,7 @@ import type { } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { chunk, flatten } from '../../../utils/utilities'; +import { chunk, flatten, getResolvables } from '@utils/utilities'; import mssql from 'mssql'; @@ -90,9 +90,10 @@ export class MicrosoftSql implements INodeType { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'mssql', + sqlDialect: 'MSSQL', }, displayOptions: { show: { @@ -293,7 +294,11 @@ export class MicrosoftSql implements INodeType { // executeQuery // ---------------------------------- - const rawQuery = this.getNodeParameter('query', 0) as string; + let rawQuery = this.getNodeParameter('query', 0) as string; + + for (const resolvable of getResolvables(rawQuery)) { + rawQuery = rawQuery.replace(resolvable, this.evaluateExpression(resolvable, 0) as string); + } const queryResult = await pool.request().query(rawQuery); diff --git a/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts b/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts index 8938fb48f8..ab25667113 100644 --- a/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts +++ b/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts @@ -17,7 +17,7 @@ import type mysql2 from 'mysql2/promise'; import { copyInputItems, createConnection, searchTables } from './GenericFunctions'; import type { IExecuteFunctions } from 'n8n-core'; -import { oldVersionNotice } from '../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; const versionDescription: INodeTypeDescription = { displayName: 'MySQL', @@ -75,9 +75,10 @@ const versionDescription: INodeTypeDescription = { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'mysql', + sqlDialect: 'MySQL', }, displayOptions: { show: { diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/deleteTable.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/deleteTable.operation.ts index 09cadea313..ef6808ca98 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/deleteTable.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/deleteTable.operation.ts @@ -9,7 +9,7 @@ import type { WhereClause, } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { addWhereClauses } from '../../helpers/utils'; diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/executeQuery.operation.ts index 853be02ac3..13c552b0a1 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/executeQuery.operation.ts @@ -4,7 +4,7 @@ import { NodeOperationError } from 'n8n-workflow'; import type { QueryRunner, QueryWithValues } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { getResolvables, updateDisplayOptions } from '@utils/utilities'; import { prepareQueryAndReplacements, replaceEmptyStringsByNulls } from '../../helpers/utils'; @@ -20,9 +20,10 @@ const properties: INodeProperties[] = [ required: true, description: "The SQL query to execute. You can use n8n expressions and $1, $2, $3, etc to refer to the 'Query Parameters' set in options below.", + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'mysql', + sqlDialect: 'MySQL', }, hint: 'Prefer using query parameters over n8n expressions to avoid SQL injection attacks', }, @@ -58,7 +59,11 @@ export async function execute( const queries: QueryWithValues[] = []; for (let i = 0; i < items.length; i++) { - const rawQuery = this.getNodeParameter('query', i) as string; + let rawQuery = this.getNodeParameter('query', i) as string; + + for (const resolvable of getResolvables(rawQuery)) { + rawQuery = rawQuery.replace(resolvable, this.evaluateExpression(resolvable, i) as string); + } const options = this.getNodeParameter('options', i, {}); diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/insert.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/insert.operation.ts index ce5c523c66..08d818d31f 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/insert.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/insert.operation.ts @@ -10,7 +10,7 @@ import type { import { AUTO_MAP, BATCH_MODE, DATA_MODE } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { copyInputItems, replaceEmptyStringsByNulls } from '../../helpers/utils'; diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/select.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/select.operation.ts index 5afbe31513..909de912f6 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/select.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/select.operation.ts @@ -9,7 +9,7 @@ import type { WhereClause, } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { addSortRules, addWhereClauses } from '../../helpers/utils'; diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/update.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/update.operation.ts index b3620e0c4b..7dd602e77f 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/update.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/update.operation.ts @@ -4,7 +4,7 @@ import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workf import type { QueryRunner, QueryValues, QueryWithValues } from '../../helpers/interfaces'; import { AUTO_MAP, DATA_MODE } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { replaceEmptyStringsByNulls } from '../../helpers/utils'; diff --git a/packages/nodes-base/nodes/MySql/v2/actions/database/upsert.operation.ts b/packages/nodes-base/nodes/MySql/v2/actions/database/upsert.operation.ts index dd50f20ba1..859686e5d6 100644 --- a/packages/nodes-base/nodes/MySql/v2/actions/database/upsert.operation.ts +++ b/packages/nodes-base/nodes/MySql/v2/actions/database/upsert.operation.ts @@ -4,7 +4,7 @@ import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workf import type { QueryRunner, QueryValues, QueryWithValues } from '../../helpers/interfaces'; import { AUTO_MAP, DATA_MODE } from '../../helpers/interfaces'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import { replaceEmptyStringsByNulls } from '../../helpers/utils'; diff --git a/packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts b/packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts index ead4afb90d..09d57ad9fc 100644 --- a/packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts +++ b/packages/nodes-base/nodes/Postgres/v1/PostgresV1.node.ts @@ -16,7 +16,7 @@ import pgPromise from 'pg-promise'; import { pgInsertV2, pgQueryV2, pgUpdate, wrapData } from './genericFunctions'; -import { oldVersionNotice } from '../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; const versionDescription: INodeTypeDescription = { displayName: 'Postgres', @@ -74,9 +74,10 @@ const versionDescription: INodeTypeDescription = { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'postgres', + sqlDialect: 'PostgreSQL', }, displayOptions: { show: { diff --git a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts index bf8548f022..edf4f02a62 100644 --- a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts +++ b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts @@ -1,6 +1,7 @@ import type { IExecuteFunctions, IDataObject, INodeExecutionData, JsonObject } from 'n8n-workflow'; import type pgPromise from 'pg-promise'; import type pg from 'pg-promise/typescript/pg-subset'; +import { getResolvables } from '@utils/utilities'; /** * Returns of a shallow copy of the items which only contains the json data and @@ -168,7 +169,10 @@ export async function pgQueryV2( db: pgPromise.IDatabase<{}, pg.IClient>, items: INodeExecutionData[], continueOnFail: boolean, - overrideMode?: string, + options?: { + overrideMode?: string; + resolveExpression?: boolean; + }, ): Promise { const additionalFields = this.getNodeParameter('additionalFields', 0); @@ -183,13 +187,22 @@ export async function pgQueryV2( type QueryWithValues = { query: string; values?: string[] }; const allQueries = new Array(); for (let i = 0; i < items.length; i++) { - const query = this.getNodeParameter('query', i) as string; + let query = this.getNodeParameter('query', i) as string; + + if (options?.resolveExpression) { + for (const resolvable of getResolvables(query)) { + query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string); + } + } + const values = valuesArray[i]; const queryFormat = { query, values }; allQueries.push(queryFormat); } - const mode = overrideMode ? overrideMode : ((additionalFields.mode ?? 'multiple') as string); + const mode = options?.overrideMode + ? options.overrideMode + : ((additionalFields.mode ?? 'multiple') as string); if (mode === 'multiple') { return (await db.multi(pgp.helpers.concat(allQueries))) .map((result, i) => { diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts index c0d4eba9a4..40c205cc4e 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts @@ -2,7 +2,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts index 1d59228f0b..8d3346ccc9 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts @@ -2,7 +2,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { getResolvables, updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, QueriesRunner, QueryWithValues } from '../../helpers/interfaces'; @@ -17,18 +17,19 @@ const properties: INodeProperties[] = [ type: 'string', default: '', placeholder: 'e.g. SELECT id, name FROM product WHERE quantity > $1 AND price <= $2', + noDataExpression: true, required: true, description: "The SQL query to execute. You can use n8n expressions and $1, $2, $3, etc to refer to the 'Query Parameters' set in options below.", typeOptions: { editor: 'sqlEditor', - sqlDialect: 'postgres', + sqlDialect: 'PostgreSQL', }, hint: 'Prefer using query parameters over n8n expressions to avoid SQL injection attacks', }, { displayName: ` - To use query parameters in your SQL query, reference them as $1, $2, $3, etc in the corresponding order. More info. + To use query parameters in your SQL query, reference them as $1, $2, $3, etc in the corresponding order. More info. `, name: 'notice', type: 'notice', @@ -58,7 +59,11 @@ export async function execute( const queries: QueryWithValues[] = []; for (let i = 0; i < items.length; i++) { - const query = this.getNodeParameter('query', i) as string; + let query = this.getNodeParameter('query', i) as string; + + for (const resolvable of getResolvables(query)) { + query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string); + } let values: IDataObject[] = []; diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts index 3fbecdee54..e6a67f4100 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts index 2844893dc9..80a9418880 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts @@ -1,7 +1,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts index 4b1f9c4c9d..32413a94e1 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/update.operation.ts @@ -2,7 +2,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts index 10a68c6541..8fcbaa6f87 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts @@ -2,7 +2,7 @@ import type { IExecuteFunctions } from 'n8n-core'; import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { updateDisplayOptions } from '../../../../../utils/utilities'; +import { updateDisplayOptions } from '@utils/utilities'; import type { PgpDatabase, diff --git a/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts b/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts index 90af31d753..9090243607 100644 --- a/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts +++ b/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts @@ -8,7 +8,7 @@ import { NodeOperationError } from 'n8n-workflow'; import pgPromise from 'pg-promise'; -import { pgInsert, pgQuery } from '../Postgres/v1/genericFunctions'; +import { pgInsert, pgQueryV2 } from '../Postgres/v1/genericFunctions'; export class QuestDb implements INodeType { description: INodeTypeDescription = { @@ -60,9 +60,10 @@ export class QuestDb implements INodeType { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'postgres', + sqlDialect: 'PostgreSQL', }, displayOptions: { show: { @@ -225,14 +226,10 @@ export class QuestDb implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', 0); const mode = (additionalFields.mode || 'independently') as string; - const queryResult = await pgQuery( - this.getNodeParameter, - pgp, - db, - items, - this.continueOnFail(), - mode, - ); + const queryResult = await pgQueryV2.call(this, pgp, db, items, this.continueOnFail(), { + overrideMode: mode, + resolveExpression: true, + }); returnItems = this.helpers.returnJsonArray(queryResult); } else if (operation === 'insert') { diff --git a/packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts b/packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts index 6bf2d6afcf..8143161239 100644 --- a/packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts +++ b/packages/nodes-base/nodes/Slack/V1/SlackV1.node.ts @@ -22,7 +22,7 @@ import { userProfileFields, userProfileOperations } from './UserProfileDescripti import { slackApiRequest, slackApiRequestAllItems, validateJSON } from './GenericFunctions'; import type { IAttachment } from './MessageInterface'; -import { oldVersionNotice } from '../../../utils/descriptions'; +import { oldVersionNotice } from '@utils/descriptions'; import moment from 'moment'; diff --git a/packages/nodes-base/nodes/Snowflake/Snowflake.node.ts b/packages/nodes-base/nodes/Snowflake/Snowflake.node.ts index 32ea9e477f..77101ee046 100644 --- a/packages/nodes-base/nodes/Snowflake/Snowflake.node.ts +++ b/packages/nodes-base/nodes/Snowflake/Snowflake.node.ts @@ -9,6 +9,7 @@ import type { import { connect, copyInputItems, destroy, execute } from './GenericFunctions'; import snowflake from 'snowflake-sdk'; +import { getResolvables } from '@utils/utilities'; export class Snowflake implements INodeType { description: INodeTypeDescription = { @@ -65,6 +66,7 @@ export class Snowflake implements INodeType { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', }, @@ -178,7 +180,12 @@ export class Snowflake implements INodeType { // ---------------------------------- for (let i = 0; i < items.length; i++) { - const query = this.getNodeParameter('query', i) as string; + let query = this.getNodeParameter('query', i) as string; + + for (const resolvable of getResolvables(query)) { + query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string); + } + responseData = await execute(connection, query, []); const executionData = this.helpers.constructExecutionMetaData( this.helpers.returnJsonArray(responseData as IDataObject[]), diff --git a/packages/nodes-base/nodes/TimescaleDb/TimescaleDb.node.ts b/packages/nodes-base/nodes/TimescaleDb/TimescaleDb.node.ts index 1d0c2c21ac..10e8259d28 100644 --- a/packages/nodes-base/nodes/TimescaleDb/TimescaleDb.node.ts +++ b/packages/nodes-base/nodes/TimescaleDb/TimescaleDb.node.ts @@ -6,7 +6,7 @@ import type { } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { pgInsert, pgQuery, pgUpdate } from '../Postgres/v1/genericFunctions'; +import { pgInsert, pgQueryV2, pgUpdate } from '../Postgres/v1/genericFunctions'; import pgPromise from 'pg-promise'; @@ -65,9 +65,10 @@ export class TimescaleDb implements INodeType { displayName: 'Query', name: 'query', type: 'string', + noDataExpression: true, typeOptions: { editor: 'sqlEditor', - sqlDialect: 'postgres', + sqlDialect: 'PostgreSQL', }, displayOptions: { show: { @@ -279,13 +280,9 @@ export class TimescaleDb implements INodeType { // executeQuery // ---------------------------------- - const queryResult = await pgQuery( - this.getNodeParameter, - pgp, - db, - items, - this.continueOnFail(), - ); + const queryResult = await pgQueryV2.call(this, pgp, db, items, this.continueOnFail(), { + resolveExpression: true, + }); returnItems = this.helpers.returnJsonArray(queryResult); } else if (operation === 'insert') { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 1f9608e46d..e260be8b82 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -17,7 +17,7 @@ "clean": "rimraf dist .turbo", "dev": "pnpm watch", "typecheck": "tsc", - "build": "tsc -p tsconfig.build.json && gulp build:icons && gulp build:translations && pnpm build:metadata", + "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && gulp build:icons && gulp build:translations && pnpm build:metadata", "build:translations": "gulp build:translations", "build:metadata": "pnpm n8n-generate-known && pnpm n8n-generate-ui-types", "format": "prettier --write . --ignore-path ../../.prettierignore", diff --git a/packages/nodes-base/test/utils/utilities.test.ts b/packages/nodes-base/test/utils/utilities.test.ts index e98ca10b20..88edcedc27 100644 --- a/packages/nodes-base/test/utils/utilities.test.ts +++ b/packages/nodes-base/test/utils/utilities.test.ts @@ -1,4 +1,4 @@ -import { fuzzyCompare, keysToLowercase, wrapData } from '../../utils/utilities'; +import { fuzzyCompare, getResolvables, keysToLowercase, wrapData } from '@utils/utilities'; //most test cases for fuzzyCompare are done in Compare Datasets node tests describe('Test fuzzyCompare', () => { @@ -101,3 +101,39 @@ describe('Test keysToLowercase', () => { expect(test6).toEqual(undefined); }); }); + +describe('Test getResolvables', () => { + it('should return empty array when there are no resolvables', () => { + expect(getResolvables('Plain String, no resolvables here.')).toEqual([]); + }); + it('should properly handle resovables in SQL query', () => { + expect(getResolvables('SELECT * FROM {{ $json.db }}.{{ $json.table }};')).toEqual([ + '{{ $json.db }}', + '{{ $json.table }}', + ]); + }); + it('should properly handle resovables in HTML string', () => { + expect( + getResolvables( + ` + + + {{ $json.pageTitle }} +

{{ $json.heading }}

+ + + + `, + ), + ).toEqual([ + '{{ $json.pageTitle }}', + '{{ $json.heading }}', + '{{ $json.pageHeight }}', + '{{ $json.welcomeMessage }}', + ]); + }); +}); diff --git a/packages/nodes-base/tsconfig.json b/packages/nodes-base/tsconfig.json index 6caa6cf9bb..2d53507ba0 100644 --- a/packages/nodes-base/tsconfig.json +++ b/packages/nodes-base/tsconfig.json @@ -5,7 +5,8 @@ "types": ["node", "jest"], "noEmit": true, "paths": { - "@test/*": ["./test/*"] + "@test/*": ["./test/*"], + "@utils/*": ["./utils/*"] }, // TODO: remove all options below this line "noImplicitReturns": false, @@ -16,5 +17,12 @@ "references": [ { "path": "../workflow/tsconfig.build.json" }, { "path": "../core/tsconfig.build.json" } - ] + ], + "tsc-alias": { + "replacers": { + "base-url": { + "enabled": false + } + } + } } diff --git a/packages/nodes-base/utils/utilities.ts b/packages/nodes-base/utils/utilities.ts index cca0d35084..4b7ce37989 100644 --- a/packages/nodes-base/utils/utilities.ts +++ b/packages/nodes-base/utils/utilities.ts @@ -214,3 +214,23 @@ export const keysToLowercase = (headers: T) => { return acc; }, {} as IDataObject); }; + +/** + * @TECH_DEBT Explore replacing with handlebars + */ +export function getResolvables(expression: string) { + if (!expression) return []; + + const resolvables = []; + const resolvableRegex = /({{[\s\S]*?}})/g; + + let match; + + while ((match = resolvableRegex.exec(expression)) !== null) { + if (match[1]) { + resolvables.push(match[1]); + } + } + + return resolvables; +} diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 2a944ddbe7..df3216de2e 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1025,7 +1025,15 @@ export type CodeAutocompleteTypes = 'function' | 'functionItem'; export type EditorType = 'code' | 'codeNodeEditor' | 'htmlEditor' | 'sqlEditor' | 'json'; export type CodeNodeEditorLanguage = (typeof CODE_LANGUAGES)[number]; export type CodeExecutionMode = (typeof CODE_EXECUTION_MODES)[number]; -export type SQLDialect = 'mssql' | 'mysql' | 'postgres'; +export type SQLDialect = + | 'StandardSQL' + | 'PostgreSQL' + | 'MySQL' + | 'MariaSQL' + | 'MSSQL' + | 'SQLite' + | 'Cassandra' + | 'PLSQL'; export interface ILoadOptions { routing?: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8082eaafff..d15f941172 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -773,9 +773,6 @@ importers: '@codemirror/lang-python': specifier: ^6.1.2 version: 6.1.2(@codemirror/state@6.1.4)(@codemirror/view@6.5.1)(@lezer/common@1.0.1) - '@codemirror/lang-sql': - specifier: ^6.4.1 - version: 6.4.1(@codemirror/view@6.5.1)(@lezer/common@1.0.1) '@codemirror/language': specifier: ^6.2.1 version: 6.2.1 @@ -818,6 +815,9 @@ importers: '@jsplumb/util': specifier: ^5.13.2 version: 5.13.2 + '@n8n/codemirror-lang-sql': + specifier: ^1.0.0 + version: 1.0.0(@codemirror/view@6.5.1)(@lezer/common@1.0.1) axios: specifier: ^0.21.1 version: 0.21.4 @@ -4128,19 +4128,6 @@ packages: - '@lezer/common' dev: false - /@codemirror/lang-sql@6.4.1(@codemirror/view@6.5.1)(@lezer/common@1.0.1): - resolution: {integrity: sha512-PFB56L+A0WGY35uRya+Trt5g19V9k2V9X3c55xoFW4RgiATr/yLqWsbbnEsdxuMn5tLpuikp7Kmj9smRsqBXAg==} - dependencies: - '@codemirror/autocomplete': 6.4.0(@codemirror/language@6.2.1)(@codemirror/state@6.1.4)(@codemirror/view@6.5.1)(@lezer/common@1.0.1) - '@codemirror/language': 6.2.1 - '@codemirror/state': 6.1.4 - '@lezer/highlight': 1.1.1 - '@lezer/lr': 1.2.3 - transitivePeerDependencies: - - '@codemirror/view' - - '@lezer/common' - dev: false - /@codemirror/language@6.2.1: resolution: {integrity: sha512-MC3svxuvIj0MRpFlGHxLS6vPyIdbTr2KKPEW46kCoCXw2ktb4NTkpkPBI/lSP/FoNXLCBJ0mrnUi1OoZxtpW1Q==} dependencies: @@ -5272,6 +5259,19 @@ packages: dev: false optional: true + /@n8n/codemirror-lang-sql@1.0.0(@codemirror/view@6.5.1)(@lezer/common@1.0.1): + resolution: {integrity: sha512-7bmlhaSW+f/g+IarWbif/D9bUgwW8bjCbjfW6BCGqZHXTz9UQt8fM6tQ9MNh/3sZz9LPwcnT7XSSv73Ku0rriw==} + dependencies: + '@codemirror/autocomplete': 6.4.0(@codemirror/language@6.2.1)(@codemirror/state@6.1.4)(@codemirror/view@6.5.1)(@lezer/common@1.0.1) + '@codemirror/language': 6.2.1 + '@codemirror/state': 6.1.4 + '@lezer/highlight': 1.1.1 + '@lezer/lr': 1.2.3 + transitivePeerDependencies: + - '@codemirror/view' + - '@lezer/common' + dev: false + /@n8n_io/license-sdk@2.4.0: resolution: {integrity: sha512-99kuCVH4NcBi4nyn/WIpd6KSIMLk/pbBks0zr8bC65ALKj0se7/2MwC6N+WwGkG7NqH0kMdGe/7Y5KnJkMTefg==} engines: {node: '>=14.0.0', npm: '>=7.10.0'}