Skip to content

Commit

Permalink
Merge branch 'master' into N8N-5308-execution-retry-popup-fix
Browse files Browse the repository at this point in the history
* master:
  🔖 Release n8n@0.202.0
  ⬆️ Set n8n-core@0.142.0, n8n-editor-ui@0.168.0, n8n-nodes-base@0.200.0 and n8n-workflow@0.124.0 on n8n
  🔖 Release n8n-editor-ui@0.168.0
  ⬆️ Set n8n-design-system@0.42.0 and n8n-workflow@0.124.0 on n8n-editor-ui
  🔖 Release n8n-design-system@0.42.0
  🔖 Release n8n-nodes-base@0.200.0
  ⬆️ Set n8n-core@0.142.0 and n8n-workflow@0.124.0 on n8n-nodes-base
  🔖 Release n8n-node-dev@0.81.0
  ⬆️ Set n8n-core@0.142.0 and n8n-workflow@0.124.0 on n8n-node-dev
  🔖 Release n8n-core@0.142.0
  ⬆️ Set n8n-workflow@0.124.0 on n8n-core
  🔖 Release n8n-workflow@0.124.0
  refactor(editor): Reintroduce `item` and `items` to `CodeNodeEditor` (#4553)
  fix(core): Streamline multiple pinned triggers behavior (#4569)
  • Loading branch information
MiloradFilipovic committed Nov 10, 2022
2 parents 78470f5 + e2b3cea commit 23445eb
Show file tree
Hide file tree
Showing 15 changed files with 144 additions and 68 deletions.
10 changes: 5 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n",
"version": "0.201.0",
"version": "0.202.0",
"description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
Expand Down Expand Up @@ -148,10 +148,10 @@
"lodash.split": "^4.4.2",
"lodash.unset": "^4.5.2",
"mysql2": "~2.3.0",
"n8n-core": "~0.141.0",
"n8n-editor-ui": "~0.167.0",
"n8n-nodes-base": "~0.199.0",
"n8n-workflow": "~0.123.0",
"n8n-core": "~0.142.0",
"n8n-editor-ui": "~0.168.0",
"n8n-nodes-base": "~0.200.0",
"n8n-workflow": "~0.124.0",
"nodemailer": "^6.7.1",
"oauth-1.0a": "^2.2.6",
"open": "^7.0.0",
Expand Down
19 changes: 3 additions & 16 deletions packages/cli/src/workflows/workflows.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-param-reassign */

import express from 'express';
import { INode, IPinData, LoggerProxy, Workflow } from 'n8n-workflow';
import { INode, LoggerProxy, Workflow } from 'n8n-workflow';

import axios from 'axios';
import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner';
Expand All @@ -18,7 +18,6 @@ import {
IWorkflowResponse,
IExecutionPushResponse,
IWorkflowExecutionDataProcess,
IWorkflowDb,
} from '@/Interfaces';
import config from '@/config';
import * as TagHelpers from '@/TagHelpers';
Expand Down Expand Up @@ -49,18 +48,6 @@ workflowsController.use((req, res, next) => {

workflowsController.use('/', EEWorkflowController);

const isTrigger = (nodeType: string) =>
['trigger', 'webhook'].some((suffix) => nodeType.toLowerCase().includes(suffix));

function findFirstPinnedTrigger(workflow: IWorkflowDb, pinData?: IPinData) {
if (!pinData) return;

// eslint-disable-next-line consistent-return
return workflow.nodes.find(
(node) => !node.disabled && isTrigger(node.type) && pinData[node.name],
);
}

/**
* POST /workflows
*/
Expand Down Expand Up @@ -351,11 +338,11 @@ workflowsController.post(

const sessionId = GenericHelpers.getSessionId(req);

const pinnedTrigger = findFirstPinnedTrigger(workflowData, pinData);
const pinnedTrigger = WorkflowsService.findPinnedTrigger(workflowData, startNodes, pinData);

// If webhooks nodes exist and are active we have to wait for till we receive a call
if (
pinnedTrigger === undefined &&
pinnedTrigger === null &&
(runData === undefined ||
startNodes === undefined ||
startNodes.length === 0 ||
Expand Down
25 changes: 24 additions & 1 deletion packages/cli/src/workflows/workflows.services.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JsonObject, jsonParse, LoggerProxy } from 'n8n-workflow';
import { IPinData, JsonObject, jsonParse, LoggerProxy } from 'n8n-workflow';
import { FindManyOptions, FindOneOptions, In, ObjectLiteral } from 'typeorm';
import { validate as jsonSchemaValidate } from 'jsonschema';
import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner';
Expand All @@ -15,6 +15,7 @@ import { validateEntity } from '@/GenericHelpers';
import { externalHooks } from '@/Server';
import * as TagHelpers from '@/TagHelpers';
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { IWorkflowDb } from '..';

export interface IGetWorkflowsQueryFilter {
id?: number | string;
Expand Down Expand Up @@ -61,6 +62,28 @@ export class WorkflowsService {
return Db.collections.SharedWorkflow.findOne(options);
}

/**
* Find the pinned trigger to execute the workflow from, if any.
*/
static findPinnedTrigger(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) {
if (!pinData || !startNodes) return null;

const isTrigger = (nodeTypeName: string) =>
['trigger', 'webhook'].some((suffix) => nodeTypeName.toLowerCase().includes(suffix));

const pinnedTriggers = workflow.nodes.filter(
(node) => !node.disabled && pinData[node.name] && isTrigger(node.type),
);

if (pinnedTriggers.length === 0) return null;

if (startNodes?.length === 0) return pinnedTriggers[0]; // full execution

const [startNodeName] = startNodes;

return pinnedTriggers.find((pt) => pt.name === startNodeName) ?? null; // partial execution
}

static async get(workflow: Partial<WorkflowEntity>, options?: { relations: string[] }) {
return Db.collections.Workflow.findOne(workflow, options);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-core",
"version": "0.141.0",
"version": "0.142.0",
"description": "Core functionality of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
Expand Down Expand Up @@ -48,7 +48,7 @@
"form-data": "^4.0.0",
"lodash.get": "^4.4.2",
"mime-types": "^2.1.27",
"n8n-workflow": "~0.123.0",
"n8n-workflow": "~0.124.0",
"oauth-1.0a": "^2.2.6",
"p-cancelable": "^2.0.0",
"qs": "^6.10.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/design-system/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-design-system",
"version": "0.41.0",
"version": "0.42.0",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
"author": {
Expand Down
6 changes: 3 additions & 3 deletions packages/editor-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-editor-ui",
"version": "0.167.0",
"version": "0.168.0",
"description": "Workflow Editor UI for n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
Expand Down Expand Up @@ -55,8 +55,8 @@
"lodash.set": "^4.3.2",
"luxon": "^2.3.0",
"monaco-editor": "^0.33.0",
"n8n-design-system": "~0.41.0",
"n8n-workflow": "~0.123.0",
"n8n-design-system": "~0.42.0",
"n8n-workflow": "~0.124.0",
"normalize-wheel": "^1.0.1",
"pinia": "^2.0.22",
"prismjs": "^1.17.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const completerExtension = mixins(
localCompletionSource,

// core
this.itemCompletions,
this.baseCompletions,
this.requireCompletions,
this.nodeSelectorCompletions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,34 @@ function getAutocompletableNodeNames(nodes: INodeUi[]) {

export const baseCompletions = (Vue as CodeNodeEditorMixin).extend({
computed: {
...mapStores(
useWorkflowsStore,
),
...mapStores(useWorkflowsStore),
},
methods: {
itemCompletions(context: CompletionContext): CompletionResult | null {
const preCursor = context.matchBefore(/i\w*/);

if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;

const options: Completion[] = [];

if (this.mode === 'runOnceForEachItem') {
options.push({
label: 'item',
info: this.$locale.baseText('codeNodeEditor.completer.$input.item'),
});
} else if (this.mode === 'runOnceForAllItems') {
options.push({
label: 'items',
info: this.$locale.baseText('codeNodeEditor.completer.$input.all'),
});
}

return {
from: preCursor.from,
options,
};
},

/**
* - Complete `$` to `$execution $input $prevNode $runIndex $workflow $now $today
* $jmespath $('nodeName')` in both modes.
Expand Down
73 changes: 54 additions & 19 deletions packages/editor-ui/src/components/CodeNodeEditor/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,19 @@ export const linterExtension = (Vue as CodeNodeEditorMixin).extend({
/**
* Lint for `.item` unavailable in `runOnceForAllItems` mode
*
* $input.all().item -> <removed>
* $input.item -> <removed>
*/

if (this.mode === 'runOnceForAllItems') {
type TargetNode = RangeNode & { property: RangeNode };

const isUnavailablePropertyinAllItems = (node: Node) =>
const isUnavailableItemAccess = (node: Node) =>
node.type === 'MemberExpression' &&
node.computed === false &&
node.property.type === 'Identifier' &&
node.property.name === 'item';

walk<TargetNode>(ast, isUnavailablePropertyinAllItems).forEach((node) => {
walk<TargetNode>(ast, isUnavailableItemAccess).forEach((node) => {
const [start, end] = this.getRange(node.property);

lintings.push({
Expand All @@ -159,39 +159,74 @@ export const linterExtension = (Vue as CodeNodeEditorMixin).extend({
}

/**
* Lint for `item` (legacy var from Function Item node) being accessed
* in `runOnceForEachItem` mode, unless user-defined `item`.
* Lint for `item` (legacy var from Function Item node) unavailable
* in `runOnceForAllItems` mode, unless user-defined `item`.
*
* item. -> $input.item.json.
* item -> $input.all()
*/
if (this.mode === 'runOnceForEachItem' && !/(let|const|var) item =/.test(script)) {
if (this.mode === 'runOnceForAllItems' && !/(let|const|var) item (=|of)/.test(script)) {
type TargetNode = RangeNode & { object: RangeNode & { name: string } };

const isItemAccess = (node: Node) =>
node.type === 'MemberExpression' &&
node.computed === false &&
node.object.type === 'Identifier' &&
node.object.name === 'item';
const isUnavailableLegacyItems = (node: Node) =>
node.type === 'Identifier' && node.name === 'item';

walk<TargetNode>(ast, isUnavailableLegacyItems).forEach((node) => {
const [start, end] = this.getRange(node);

lintings.push({
from: start,
to: end,
severity: DEFAULT_LINTER_SEVERITY,
message: this.$locale.baseText('codeNodeEditor.linter.allItems.unavailableItem'),
actions: [
{
name: 'Fix',
apply(view, from, to) {
// prevent second insertion of unknown origin
if (view.state.doc.toString().slice(from, to).includes('$input.all()')) {
return;
}

view.dispatch({ changes: { from: start, to: end } });
view.dispatch({ changes: { from, insert: '$input.all()' } });
},
},
],
});
});
}

/**
* Lint for `items` (legacy var from Function node) unavailable
* in `runOnceForEachItem` mode, unless user-defined `items`.
*
* items -> $input.item
*/
if (this.mode === 'runOnceForEachItem' && !/(let|const|var) items =/.test(script)) {
type TargetNode = RangeNode & { object: RangeNode & { name: string } };

walk<TargetNode>(ast, isItemAccess).forEach((node) => {
const [start, end] = this.getRange(node.object);
const isUnavailableLegacyItems = (node: Node) =>
node.type === 'Identifier' && node.name === 'items';

walk<TargetNode>(ast, isUnavailableLegacyItems).forEach((node) => {
const [start, end] = this.getRange(node);

lintings.push({
from: start,
to: end,
severity: DEFAULT_LINTER_SEVERITY,
message: this.$locale.baseText('codeNodeEditor.linter.eachItem.legacyItemAccess'),
message: this.$locale.baseText('codeNodeEditor.linter.eachItem.unavailableItems'),
actions: [
{
name: 'Fix',
apply(view, from, to) {
// prevent second insertion of unknown origin
if (view.state.doc.toString().slice(from, to).includes('$input.item.json')) {
if (view.state.doc.toString().slice(from, to).includes('$input.item')) {
return;
}

view.dispatch({ changes: { from: start, to: end } });
view.dispatch({ changes: { from, insert: '$input.item.json' } });
view.dispatch({ changes: { from, insert: '$input.item' } });
},
},
],
Expand Down Expand Up @@ -285,8 +320,8 @@ export const linterExtension = (Vue as CodeNodeEditorMixin).extend({
node.callee.object.type === 'Identifier' &&
node.callee.object.name === '$input' &&
node.callee.property.type === 'Identifier' &&
['first', 'last'].includes(node.callee.property.name)
&& node.arguments.length !== 0;
['first', 'last'].includes(node.callee.property.name) &&
node.arguments.length !== 0;

walk<TargetNode>(ast, inputFirstOrLastCalledWithArg).forEach((node) => {
const [start, end] = this.getRange(node.callee.property);
Expand Down
4 changes: 3 additions & 1 deletion packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,17 @@
"codeNodeEditor.linter.allItems.emptyReturn": "Code doesn't return items properly. Please return an array of objects, one for each item you would like to output.",
"codeNodeEditor.linter.allItems.itemCall": "`item` is a property to access, not a method to call. Did you mean `.item` without brackets?",
"codeNodeEditor.linter.allItems.itemMatchingNoArg": "`.itemMatching()` expects an item index to be passed in as its argument.",
"codeNodeEditor.linter.allItems.unavailableItem": "Legacy `item` is only available in the 'Run Once for Each Item' mode.",
"codeNodeEditor.linter.allItems.unavailableProperty": "`.item` is only available in the 'Run Once for Each Item' mode.",
"codeNodeEditor.linter.allItems.unavailableVar": "is only available in the 'Run Once for Each Item' mode.",
"codeNodeEditor.linter.bothModes.directAccess.firstOrLastCall": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
"codeNodeEditor.linter.bothModes.directAccess.itemProperty": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
"codeNodeEditor.linter.bothModes.varDeclaration.itemProperty": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
"codeNodeEditor.linter.bothModes.varDeclaration.itemSubproperty": "@:_reusableBaseText.codeNodeEditor.linter.useJson",
"codeNodeEditor.linter.eachItem.emptyReturn": "Code doesn't return an object. Please return an object representing the output item",
"codeNodeEditor.linter.eachItem.legacyItemAccess": "`item` is not defined. Did you mean `$input.item.json`?",
"codeNodeEditor.linter.eachItem.legacyItemAccess": "`item` is a legacy var. Consider using `$input.item`",
"codeNodeEditor.linter.eachItem.returnArray": "Code doesn't return an object. Array found instead. Please return an object representing the output item",
"codeNodeEditor.linter.eachItem.unavailableItems": "Legacy `items` is only available in the 'Run Once for All Items' mode.",
"codeNodeEditor.linter.eachItem.unavailableMethod": "Method `$input.{method}()` is only available in the 'Run Once for All Items' mode.",
"codeNodeEditor.linter.bothModes.syntaxError": "Syntax error",
"collectionParameter.choose": "Choose...",
Expand Down
6 changes: 3 additions & 3 deletions packages/node-dev/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-node-dev",
"version": "0.80.0",
"version": "0.81.0",
"description": "CLI to simplify n8n credentials/node development",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
Expand Down Expand Up @@ -59,8 +59,8 @@
"change-case": "^4.1.1",
"fast-glob": "^3.2.5",
"inquirer": "^7.0.1",
"n8n-core": "~0.141.0",
"n8n-workflow": "~0.123.0",
"n8n-core": "~0.142.0",
"n8n-workflow": "~0.124.0",
"oauth-1.0a": "^2.2.6",
"replace-in-file": "^6.0.0",
"request": "^2.88.2",
Expand Down
2 changes: 2 additions & 0 deletions packages/nodes-base/nodes/Code/Code.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class Code implements INodeType {
const jsCodeAllItems = this.getNodeParameter('jsCode', 0) as string;

const context = getSandboxContext.call(this);
context.items = context.$input.all();
const sandbox = new Sandbox(context, workflowMode, nodeMode);

if (workflowMode === 'manual') {
Expand Down Expand Up @@ -111,6 +112,7 @@ export class Code implements INodeType {
const jsCodeEachItem = this.getNodeParameter('jsCode', index) as string;

const context = getSandboxContext.call(this, index);
context.item = context.$input.item;
const sandbox = new Sandbox(context, workflowMode, nodeMode);

if (workflowMode === 'manual') {
Expand Down
Loading

0 comments on commit 23445eb

Please sign in to comment.