diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..c2caf18ab3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Feature request + url: https://community.n8n.io + about: Suggest an idea for this project + - name: Question / Problem + url: https://community.n8n.io + about: Questions and problems with n8n diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 23b199508d..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Please do not create a GitHub issue for feature requests** - -Post all of them to the forum: -[https://community.n8n.io](https://community.n8n.io) - -They get all collected there and people can upvote existing requests. That makes it then easier to know how to prioritize them. Thanks! diff --git a/.github/ISSUE_TEMPLATE/question---problem.md b/.github/ISSUE_TEMPLATE/question---problem.md deleted file mode 100644 index c8e334e5f2..0000000000 --- a/.github/ISSUE_TEMPLATE/question---problem.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Question / Problem -about: Questions and problems with n8n -title: '' -labels: '' -assignees: '' - ---- - -**Please do not create a GitHub issue for questions or problems** - -Post all of them to the forum: -[https://community.n8n.io](https://community.n8n.io) - -Check there first if it got answered already before and if not create a new topic. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b5ff8c6c3..d07ff701a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ The most important directories: - [/packages/editor-ui](/packages/editor-ui) - Vue frontend workflow editor - [/packages/node-dev](/packages/node-dev) - Simple CLI to create new n8n-nodes - [/packages/nodes-base](/packages/nodes-base) - Base n8n nodes - - [/packages/worflow](/packages/worflow) - Workflow code with interfaces which + - [/packages/workflow](/packages/workflow) - Workflow code with interfaces which get used by front- & backend diff --git a/docs/create-node.md b/docs/create-node.md index f4dad6883a..488e1560ea 100644 --- a/docs/create-node.md +++ b/docs/create-node.md @@ -123,7 +123,7 @@ Some third-party services have their own libraries on npm which make it a little const response = await this.helpers.request(options); ``` -That is simply using the npm package `request-promise-native` which is the basic npm `request` module but with promises. +That is simply using the npm package [`request-promise-native`](https://github.com/request/request-promise-native) which is the basic npm `request` module but with promises. For a full set of `options` consider looking at [the underlying `request` options documentation](https://github.com/request/request#requestoptions-callback). ### Reuse parameter names diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index 56773f5b29..62e6615897 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -5,7 +5,8 @@ import { } from "n8n-core"; import { Command, flags } from '@oclif/command'; const open = require('open'); -import { promisify } from "util"; +import { promisify } from 'util'; +import { dirname } from 'path'; import * as config from '../config'; import { @@ -21,6 +22,10 @@ import { const tunnel = promisify(localtunnel); +// // Add support for internationalization +// const fullIcuPath = require.resolve('full-icu'); +// process.env.NODE_ICU_DATA = dirname(fullIcuPath); + let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined; let processExistCode = 0; diff --git a/packages/cli/package.json b/packages/cli/package.json index eb6d08b804..106392b64c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.35.0", + "version": "0.36.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -59,6 +59,7 @@ "@types/express": "^4.16.1", "@types/jest": "^24.0.18", "@types/localtunnel": "^1.9.0", + "@types/lodash.get": "^4.4.2", "@types/node": "^10.10.1", "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", @@ -71,9 +72,9 @@ "typescript": "~3.5.2" }, "dependencies": { - "@types/jsonwebtoken": "^8.3.4", "@oclif/command": "^1.5.18", "@oclif/errors": "^1.2.2", + "@types/jsonwebtoken": "^8.3.4", "basic-auth": "^2.0.1", "body-parser": "^1.18.3", "compression": "^1.7.4", @@ -88,11 +89,12 @@ "jsonwebtoken": "^8.5.1", "jwks-rsa": "^1.6.0", "localtunnel": "^1.9.1", + "lodash.get": "^4.4.2", "mongodb": "^3.2.3", - "n8n-core": "~0.15.0", - "n8n-editor-ui": "~0.25.0", - "n8n-nodes-base": "~0.30.0", - "n8n-workflow": "~0.15.0", + "n8n-core": "~0.16.0", + "n8n-editor-ui": "~0.26.0", + "n8n-nodes-base": "~0.31.0", + "n8n-workflow": "~0.16.0", "open": "^6.1.0", "pg": "^7.11.0", "request-promise-native": "^1.0.7", diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index 238c05fc6c..77ef8087be 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -28,6 +28,7 @@ import { IRunExecutionData, ITaskData, IWebhookData, + IWebhookResponseData, IWorkflowExecuteAdditionalData, NodeHelpers, Workflow, @@ -76,6 +77,8 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo let parentNodes: string[] | undefined; if (destinationNode !== undefined) { parentNodes = workflow.getParentNodes(destinationNode); + // Also add the destination node in case it itself is a webhook node + parentNodes.push(destinationNode); } for (const node of Object.values(workflow.nodes)) { @@ -122,7 +125,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo // If the mode is not known we error. Is probably best like that instead of using // the default that people know as early as possible (probably already testing phase) // that something does not resolve properly. - const errorMessage = `The response mode ${responseMode} is not valid!.`; + const errorMessage = `The response mode ${responseMode} is not valid!`; responseCallback(new Error(errorMessage), {}); throw new ResponseHelper.ResponseError(errorMessage, 500, 500); } @@ -136,12 +139,41 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo additionalData.httpResponse = res; let didSendResponse = false; + let runExecutionDataMerge = {}; try { // Run the webhook function to see what should be returned and if // the workflow should be executed or not - const webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); + let webhookResultData: IWebhookResponseData; - if (webhookResultData.noWebhookResponse === true) { + try { + webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); + } catch (e) { + // Send error response to webhook caller + const errorMessage = 'Workflow Webhook Error: Workflow could not be started!'; + responseCallback(new Error(errorMessage), {}); + didSendResponse = true; + + // Add error to execution data that it can be logged and send to Editor-UI + runExecutionDataMerge = { + resultData: { + runData: {}, + lastNodeExecuted: workflowStartNode.name, + error: { + message: e.message, + stack: e.stack, + }, + }, + }; + + webhookResultData = { + noWebhookResponse: true, + // Add empty data that it at least tries to "execute" the webhook + // which then so gets the chance to throw the error. + workflowData: [[{json: {}}]], + }; + } + + if (webhookResultData.noWebhookResponse === true && didSendResponse === false) { // The response got already send responseCallback(null, { noWebhookResponse: true, @@ -153,18 +185,24 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo // Workflow should not run if (webhookResultData.webhookResponse !== undefined) { // Data to respond with is given - responseCallback(null, { - data: webhookResultData.webhookResponse, - responseCode, - }); + if (didSendResponse === false) { + responseCallback(null, { + data: webhookResultData.webhookResponse, + responseCode, + }); + didSendResponse = true; + } } else { // Send default response - responseCallback(null, { - data: { - message: 'Webhook call got received.', - }, - responseCode, - }); + if (didSendResponse === false) { + responseCallback(null, { + data: { + message: 'Webhook call got received.', + }, + responseCode, + }); + didSendResponse = true; + } } return; } @@ -215,6 +253,11 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo }, }; + if (Object.keys(runExecutionDataMerge).length !== 0) { + // If data to merge got defined add it to the execution data + Object.assign(runExecutionData, runExecutionDataMerge); + } + const runData: IWorkflowExecutionDataProcess = { credentials, executionMode, diff --git a/packages/core/package.json b/packages/core/package.json index 7d5f6c7833..ff8376890c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.15.0", + "version": "0.16.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -42,7 +42,7 @@ "crypto-js": "^3.1.9-1", "lodash.get": "^4.4.2", "mmmagic": "^0.5.2", - "n8n-workflow": "~0.15.0", + "n8n-workflow": "~0.16.0", "request-promise-native": "^1.0.7" }, "jest": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 193e6cf8d8..8f5387baca 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.25.0", + "version": "0.26.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -59,7 +59,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.15.0", + "n8n-workflow": "~0.16.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", @@ -71,7 +71,7 @@ "typescript": "~3.5.2", "vue": "^2.6.9", "vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0", - "vue-json-pretty": "^1.4.1", + "vue-json-tree": "^0.4.1", "vue-prism-editor": "^0.3.0", "vue-router": "^3.0.6", "vue-template-compiler": "^2.5.17", diff --git a/packages/editor-ui/src/components/CredentialsInput.vue b/packages/editor-ui/src/components/CredentialsInput.vue index 2f45ae8928..0830b2a2a0 100644 --- a/packages/editor-ui/src/components/CredentialsInput.vue +++ b/packages/editor-ui/src/components/CredentialsInput.vue @@ -22,8 +22,12 @@ - + {{parameter.displayName}}: + +
+ +
@@ -299,6 +303,20 @@ export default mixins( .parameter-wrapper { line-height: 3em; + + .parameter-name { + position: relative; + + &:hover { + .parameter-info { + display: inline; + } + } + + .parameter-info { + display: none; + } + } } .credentials-info { diff --git a/packages/editor-ui/src/components/ParameterInput.vue b/packages/editor-ui/src/components/ParameterInput.vue index 29dba2cbe5..51b139baa5 100644 --- a/packages/editor-ui/src/components/ParameterInput.vue +++ b/packages/editor-ui/src/components/ParameterInput.vue @@ -326,11 +326,20 @@ export default mixins( // and the error is not displayed on the node in the workflow const validOptions = this.parameterOptions!.map((options: INodePropertyOptions) => options.value); - if (this.displayValue === null || !validOptions.includes(this.displayValue as string)) { - if (issues.parameters === undefined) { - issues.parameters = {}; + const checkValues: string[] = []; + if (Array.isArray(this.displayValue)) { + checkValues.push.apply(checkValues, this.displayValue); + } else { + checkValues.push(this.displayValue as string); + } + + for (const checkValue of checkValues) { + if (checkValue === null || !validOptions.includes(checkValue)) { + if (issues.parameters === undefined) { + issues.parameters = {}; + } + issues.parameters[this.parameter.name] = [`The value "${checkValue}" is not supported!`]; } - issues.parameters[this.parameter.name] = [`The value "${this.displayValue}" is not supported!`]; } } else if (this.remoteParameterOptionsLoadingIssues !== null) { if (issues.parameters === undefined) { diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index 8e81d5e197..54252b6fbe 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -95,11 +95,12 @@ - - + />
@@ -162,7 +163,7 @@