From 6d811f0d9f503905ed74938f97f193c7c3d8e7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 2 Feb 2023 12:35:38 +0100 Subject: [PATCH] feat(editor): Completions for extensions in expression editor (#5130) * :fire: Remove test extensions * :construction: Add test description * :blue_book: Expand types * :zap: Export extensions * :zap: Export collection * :zap: Mark all proxies * :pencil2: Rename for clarity * :zap: Export from barrel * :sparkles: Create datatype completions * :zap: Mount datatype completions * :test_tube: Adjust tests * :zap: Add `path` prop * :fire: Remove `()` from completion labels * :zap: Filter out completions for pseudo-proxies * :bug: Fix method error * :zap: Add metrics * :pencil2: Improve naming * :sparkles: Start completion on empty resolvable * :sparkles: Implement completion previews * :zap: Break out completion manager * :zap: Implement in expression editor modal * :pencil2: Improve naming * :zap: Filter out irrelevant completions * :sparkles: Add preview hint * :pencil2: Improve comments * :art: Style preview hint * :zap: Expand `hasNoParams` * :zap: Add spacing for readability * :zap: Add error codes * :pencil2: Add comment * :bug: Fix Esc behavior * :zap: Parse Unicode * :zap: Throw on invalid `DateTime` * :zap: Fix second root completion detection * :zap: Switch message at completable prefix position * :bug: Fix function names for non-dev build * :bug: Fix `json` handling * :fire: Comment out previews * :recycle: Apply feedback * :fire: Remove extensions * :truck: Rename extensions * :zap: Adjust some implementations * :fire: Remove dummy extensions * :bug: Fix object regex * :recycle: Apply feedback * :pencil2: Fix typos * :pencil2: Add `fn is not a function` message * :fire: Remove check * :sparkles: Add `isNotEmpty` for objects * :truck: Rename `global` to `alpha` * :fire: Remove `encrypt` * :zap: Restore `is not a function` error * :zap: Support `week` on `extract()` * :test_tube: Fix tests * :zap: Add validation to some string extensions * :zap: Validate number arrays in some extensions * :test_tube: Fix tests * :pencil2: Improve error message * :rewind: Revert extensions framework changes * :broom: Previews cleanup * :zap: Condense blank completions * :zap: Refactor dollar completions * :zap: Refactor non-dollar completions * :zap: Refactor Luxon completions * :zap: Refactor datatype completions * :zap: Use `DATETIMEUNIT_MAP` * :pencil2: Update test description * :rewind: Revert "Use `DATETIMEUNIT_MAP`" This reverts commit 472a77df5cd789905d162f3c3db02ac767b89b4e. * :test_tube: Add tests * :recycle: Restore generic extensions * :fire: Remove logs * :test_tube: Expand tests * :sparkles: Add `Math` completions * :pencil2: List breaking change * :zap: Add doc tooltips * :bug: Fix node selector regex * :bug: Fix `context` resolution * :bug: Allow dollar completions in args * :zap: Make numeric array methods context-dependent * :pencil: Adjust docs * :bug: Fix selector ref * :zap: Surface error for valid URL * :bug: Disallow whitespace in `isEmail` check * :test_tube: Fix test for `isUrl` * :zap: Add comma validator in `toFloat` * :zap: Add validation to `$jmespath()` * :rewind: Revert valid URL error * :zap: Adjust `$jmespath()` validation * :test_tube: Adjust `isUrl` test * :zap: Remove `{}` and `[]` from compact * :pencil2: Update docs * :truck: Rename `stripTags` to `removeTags` * :zap: Do not inject whitespace inside resolvable * :zap: Make completions aware of `()` * :pencil2: Add note * :zap: Update sorting * :zap: Hide active node name from node selector * :fire: Remove `length()` and its aliases * :zap: Validate non-zero for `chunk` * :pencil2: Reword all error messages * :bug: Fix `$now` and `$today` * :zap: Simplify with `stripExcessParens` * :zap: Fold luxon into datatype * :test_tube: Clean up tests * :fire: Remove tests for removed methods * :shirt: Fix type * :arrow_up: Upgrade lang pack * :rewind: Undo change to `vitest` command * :fire: Remove unused method * :zap: Separate `return` line * :pencil2: Improve description * :test_tube: Expand tests for initial-only completions * :test_tube: Add bracket-aware completions * :zap: Make check for `all()` stricter * :pencil2: Adjust explanatory comments * :fire: Remove unneded copy * :fire: Remove outdated comment * :zap: Make naming consistent * :pencil2: Update comments * :zap: Improve URL scheme check * :pencil2: Add comment * :truck: Move extension * :pencil2: Update `BREAKING-CHANGES.md` * :pencil2: Update upcoming version * :pencil2: Fix grammar * :pencil2: Shorten message * :bug: Fix `Esc` behavior * :bug: Fix `isNumeric` * :sparkles: Support native methods * :test_tube: Skip Pinia tests * :pencil2: Shorten description * :fire: Remove outdated comment * :test_tube: Unskip Pinia tests * :pencil2: Add comments * :test_tube: Expand tests to natives * :pencil2: Add clarifying comments * :zap: Use `setTimeout` to make telemetry non-blocking * :bug: Account for no active node in cred modal * :sparkles: Resolve without workflow * :fire: Remove `Esc` handling on NDV * :zap: Use `isDateTime` * :truck: Move `unique` to next phase This array extension takes optional args. * :zap: Merge export * :test_tube: Fix tests * :rewind: Restore check * :pencil2: Make breaking change description more accurate * :test_tube: Fix e2e tests --- cypress/e2e/11-inline-expression-editor.cy.ts | 10 +- cypress/e2e/9-expression-editor-modal.cy.ts | 30 +- packages/cli/BREAKING-CHANGES.md | 10 + packages/editor-ui/package.json | 2 +- .../src/components/ExpressionEdit.vue | 1 + .../ExpressionEditorModalInput.vue | 50 ++- .../components/ExpressionParameterInput.vue | 4 + .../InlineExpressionEditorInput.vue | 46 ++- .../src/components/ParameterInput.vue | 1 + .../ResourceLocator/ResourceLocator.vue | 1 + .../editor-ui/src/mixins/completionManager.ts | 76 ++++ .../editor-ui/src/mixins/expressionManager.ts | 113 +++--- .../editor-ui/src/mixins/workflowHelpers.ts | 1 + .../__tests__/alpha.completions.test.ts | 30 -- .../completions/__tests__/completions.test.ts | 360 ++++++++++++++++++ .../__tests__/luxon.completions.test.ts | 37 -- .../codemirror/completions/__tests__/mock.ts | 331 ++++++++++++++++ .../__tests__/proxy.completions.test.ts | 146 ------- .../completions/__tests__/proxyMocks.ts | 97 ----- .../__tests__/root.completions.test.ts | 80 ---- .../completions/alpha.completions.ts | 50 --- .../completions/blank.completions.ts | 24 ++ .../completions/bracketAccess.completions.ts | 74 ++++ .../completions/datatype.completions.ts | 308 +++++++++++++++ .../completions/dollar.completions.ts | 79 ++++ .../completions/luxon.completions.ts | 84 ---- .../completions/nonDollar.completions.ts | 44 +++ .../completions/proxy.completions.ts | 106 ------ .../completions/root.completions.ts | 60 --- .../plugins/codemirror/completions/types.ts | 8 + .../plugins/codemirror/completions/utils.ts | 103 ++++- .../inputHandlers/expression.inputHandler.ts | 17 +- .../src/plugins/codemirror/n8nLang.ts | 21 +- packages/editor-ui/src/plugins/i18n/index.ts | 3 +- .../src/plugins/i18n/locales/en.json | 2 + packages/editor-ui/src/types/expressions.ts | 2 +- packages/workflow/package.json | 1 + packages/workflow/src/Expression.ts | 44 ++- packages/workflow/src/ExpressionError.ts | 7 + .../src/Extensions/ArrayExtensions.ts | 190 ++++++--- .../workflow/src/Extensions/DateExtensions.ts | 129 +++---- .../src/Extensions/ExpressionExtension.ts | 12 +- .../workflow/src/Extensions/Extensions.ts | 10 +- .../src/Extensions/NumberExtensions.ts | 62 +-- .../src/Extensions/ObjectExtensions.ts | 65 +++- .../src/Extensions/StringExtensions.ts | 267 ++++++++++--- packages/workflow/src/Extensions/index.ts | 3 + .../src/NativeMethods/String.methods.ts | 13 + packages/workflow/src/NativeMethods/index.ts | 5 + packages/workflow/src/WorkflowDataProxy.ts | 28 +- packages/workflow/src/index.ts | 5 + .../ArrayExtensions.test.ts | 61 +-- .../DateExtensions.test.ts | 5 - .../ExpressionExtension.test.ts | 46 ++- .../GenericExtensions.test.ts | 4 +- .../NumberExtensions.test.ts | 30 +- .../StringExtensions.test.ts | 100 +---- pnpm-lock.yaml | 11 +- 58 files changed, 2269 insertions(+), 1240 deletions(-) create mode 100644 packages/editor-ui/src/mixins/completionManager.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/alpha.completions.test.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/luxon.completions.test.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/mock.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/proxy.completions.test.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/proxyMocks.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/__tests__/root.completions.test.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/alpha.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/blank.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/bracketAccess.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/dollar.completions.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/luxon.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/nonDollar.completions.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/proxy.completions.ts delete mode 100644 packages/editor-ui/src/plugins/codemirror/completions/root.completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/completions/types.ts create mode 100644 packages/workflow/src/NativeMethods/String.methods.ts create mode 100644 packages/workflow/src/NativeMethods/index.ts diff --git a/cypress/e2e/11-inline-expression-editor.cy.ts b/cypress/e2e/11-inline-expression-editor.cy.ts index 655c87f114..cbc4736e2b 100644 --- a/cypress/e2e/11-inline-expression-editor.cy.ts +++ b/cypress/e2e/11-inline-expression-editor.cy.ts @@ -36,12 +36,16 @@ describe('Inline expression editor', () => { it('should resolve object resolvables', () => { WorkflowPage.getters.inlineExpressionEditorInput().type('{{'); - WorkflowPage.getters.inlineExpressionEditorInput().type('{{} a: 1'); - WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^\[Object: \{"a":1\}\]$/); + WorkflowPage.getters + .inlineExpressionEditorInput() + .type('{ a: 1 }', { parseSpecialCharSequences: false }); + WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^\[Object: \{"a": 1\}\]$/); WorkflowPage.getters.inlineExpressionEditorInput().clear(); WorkflowPage.getters.inlineExpressionEditorInput().type('{{'); - WorkflowPage.getters.inlineExpressionEditorInput().type('{{} a: 1 }.a{del}{del}'); + WorkflowPage.getters + .inlineExpressionEditorInput() + .type('{ a: 1 }.a', { parseSpecialCharSequences: false }); WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^1$/); }); diff --git a/cypress/e2e/9-expression-editor-modal.cy.ts b/cypress/e2e/9-expression-editor-modal.cy.ts index 19bf0dd3ab..1a8909872d 100644 --- a/cypress/e2e/9-expression-editor-modal.cy.ts +++ b/cypress/e2e/9-expression-editor-modal.cy.ts @@ -17,49 +17,45 @@ describe('Expression editor modal', () => { }); it('should resolve primitive resolvables', () => { - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('1 + 2'); + WorkflowPage.getters.expressionModalInput().type('{{ 1 + 2'); WorkflowPage.getters.expressionModalOutput().contains(/^3$/); WorkflowPage.getters.expressionModalInput().clear(); - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('"ab" + "cd"'); + WorkflowPage.getters.expressionModalInput().type('{{ "ab" + "cd"'); WorkflowPage.getters.expressionModalOutput().contains(/^abcd$/); WorkflowPage.getters.expressionModalInput().clear(); - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('true && false'); + WorkflowPage.getters.expressionModalInput().type('{{ true && false'); WorkflowPage.getters.expressionModalOutput().contains(/^false$/); }); it('should resolve object resolvables', () => { - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('{{} a: 1'); - WorkflowPage.getters.expressionModalOutput().contains(/^\[Object: \{"a":1\}\]$/); + WorkflowPage.getters + .expressionModalInput() + .type('{{ { a : 1 }', { parseSpecialCharSequences: false }); + WorkflowPage.getters.expressionModalOutput().contains(/^\[Object: \{"a": 1\}\]$/); WorkflowPage.getters.expressionModalInput().clear(); - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('{{} a: 1 }.a{del}{del}'); + WorkflowPage.getters + .expressionModalInput() + .type('{{ { a : 1 }.a', { parseSpecialCharSequences: false }); WorkflowPage.getters.expressionModalOutput().contains(/^1$/); }); it('should resolve array resolvables', () => { - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('[1, 2, 3]'); + WorkflowPage.getters.expressionModalInput().type('{{ [1, 2, 3]'); WorkflowPage.getters.expressionModalOutput().contains(/^\[Array: \[1,2,3\]\]$/); WorkflowPage.getters.expressionModalInput().clear(); - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('[1, 2, 3][0]'); + WorkflowPage.getters.expressionModalInput().type('{{ [1, 2, 3][0]'); WorkflowPage.getters.expressionModalOutput().contains(/^1$/); }); it('should resolve $parameter[]', () => { - WorkflowPage.getters.expressionModalInput().type('{{'); - WorkflowPage.getters.expressionModalInput().type('$parameter["operation"]'); + WorkflowPage.getters.expressionModalInput().type('{{ $parameter["operation"]'); WorkflowPage.getters.expressionModalOutput().contains(/^get$/); }); }); diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index d762f824da..624ddacb42 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -2,6 +2,16 @@ This list shows all the versions which include breaking changes and how to upgrade. +## 0.214.0 + +### What changed? + +Invalid Luxon datetimes no longer resolve to `null`. Now they throw the error `invalid DateTime`. + +### When is action necessary? + +If you are relying on the above behavior, review your workflow to ensure you handle invalid Luxon datetimes. + ## 0.202.0 ### What changed? diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 2a0d066bf3..0caf329d6c 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -46,7 +46,7 @@ "@jsplumb/util": "^5.13.2", "axios": "^0.21.1", "codemirror-lang-html-n8n": "^1.0.0", - "codemirror-lang-n8n-expression": "^0.1.0", + "codemirror-lang-n8n-expression": "^0.2.0", "dateformat": "^3.0.3", "esprima-next": "5.8.4", "fast-json-stable-stringify": "^2.1.0", diff --git a/packages/editor-ui/src/components/ExpressionEdit.vue b/packages/editor-ui/src/components/ExpressionEdit.vue index 2baf3f9900..32daae6fda 100644 --- a/packages/editor-ui/src/components/ExpressionEdit.vue +++ b/packages/editor-ui/src/components/ExpressionEdit.vue @@ -46,6 +46,7 @@ -
+