From cefec7739b6da820d64f9476476e1901d4f386bf Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 22 Mar 2023 12:05:07 +0100 Subject: [PATCH 1/9] fix(Split In Batches Node): Roll back changes in v1 and create v2 (#5747) --- .../SplitInBatches/SplitInBatches.node.ts | 181 +++--------------- .../test/SplitInBatches.combineData.json | 2 +- ...w.json => SplitInBatches.workflow_v1.json} | 0 .../test/SplitInBatches.workflow_v2.json | 178 +++++++++++++++++ .../v1/SplitInBatchesV1.node.ts | 143 ++++++++++++++ .../v2/SplitInBatchesV2.node.ts | 161 ++++++++++++++++ 6 files changed, 506 insertions(+), 159 deletions(-) rename packages/nodes-base/nodes/SplitInBatches/test/{SplitInBatches.workflow.json => SplitInBatches.workflow_v1.json} (100%) create mode 100644 packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v2.json create mode 100644 packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts create mode 100644 packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts diff --git a/packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts b/packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts index c6ec5342e3209..15add2e03773b 100644 --- a/packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts +++ b/packages/nodes-base/nodes/SplitInBatches/SplitInBatches.node.ts @@ -1,160 +1,25 @@ -import type { - IExecuteFunctions, - INodeExecutionData, - INodeType, - INodeTypeDescription, - IPairedItemData, -} from 'n8n-workflow'; -import { deepCopy } from 'n8n-workflow'; - -export class SplitInBatches implements INodeType { - description: INodeTypeDescription = { - displayName: 'Split In Batches', - name: 'splitInBatches', - icon: 'fa:th-large', - group: ['organization'], - version: 1, - description: 'Split data into batches and iterate over each batch', - defaults: { - name: 'Split In Batches', - color: '#007755', - }, - inputs: ['main'], - // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong - outputs: ['main', 'main'], - outputNames: ['loop', 'done'], - properties: [ - { - displayName: - 'You may not need this node — n8n nodes automatically run once for each input item. More info', - name: 'splitInBatchesNotice', - type: 'notice', - default: '', - }, - { - displayName: 'Batch Size', - name: 'batchSize', - type: 'number', - typeOptions: { - minValue: 1, - }, - default: 10, - description: 'The number of items to return with each call', - }, - { - displayName: 'Options', - name: 'options', - type: 'collection', - placeholder: 'Add Option', - default: {}, - options: [ - { - displayName: 'Reset', - name: 'reset', - type: 'boolean', - default: false, - description: - 'Whether the node will be reset and so with the current input-data newly initialized', - }, - ], - }, - ], - }; - - async execute(this: IExecuteFunctions): Promise { - // Get the input data and create a new array so that we can remove - // items without a problem - const items = this.getInputData().slice(); - - const nodeContext = this.getContext('node'); - - const batchSize = this.getNodeParameter('batchSize', 0) as number; - - const returnItems: INodeExecutionData[] = []; - - const options = this.getNodeParameter('options', 0, {}); - - if (nodeContext.items === undefined || options.reset === true) { - // Is the first time the node runs - - const sourceData = this.getInputSourceData(); - - nodeContext.currentRunIndex = 0; - nodeContext.maxRunIndex = Math.ceil(items.length / batchSize); - nodeContext.sourceData = deepCopy(sourceData); - - // Get the items which should be returned - returnItems.push.apply(returnItems, items.splice(0, batchSize)); - - // Save the incoming items to be able to return them for later runs - nodeContext.items = [...items]; - - // Reset processedItems as they get only added starting from the first iteration - nodeContext.processedItems = []; - } else { - // The node has been called before. So return the next batch of items. - nodeContext.currentRunIndex += 1; - returnItems.push.apply( - returnItems, - (nodeContext.items as INodeExecutionData[]).splice(0, batchSize), - ); - - const addSourceOverwrite = (pairedItem: IPairedItemData | number): IPairedItemData => { - if (typeof pairedItem === 'number') { - return { - item: pairedItem, - sourceOverwrite: nodeContext.sourceData, - }; - } - - return { - ...pairedItem, - sourceOverwrite: nodeContext.sourceData, - }; - }; - - function getPairedItemInformation( - item: INodeExecutionData, - ): IPairedItemData | IPairedItemData[] { - if (item.pairedItem === undefined) { - return { - item: 0, - sourceOverwrite: nodeContext.sourceData, - }; - } - - if (Array.isArray(item.pairedItem)) { - return item.pairedItem.map(addSourceOverwrite); - } - - return addSourceOverwrite(item.pairedItem); - } - - const sourceOverwrite = this.getInputSourceData(); - - const newItems = items.map((item, index) => { - return { - ...item, - pairedItem: { - sourceOverwrite, - item: index, - }, - }; - }); - - nodeContext.processedItems = [...nodeContext.processedItems, ...newItems]; - - returnItems.map((item) => { - item.pairedItem = getPairedItemInformation(item); - }); - } - - nodeContext.noItemsLeft = nodeContext.items.length === 0; - - if (returnItems.length === 0) { - return [[], nodeContext.processedItems]; - } - - return [returnItems, []]; +import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow'; +import { VersionedNodeType } from 'n8n-workflow'; + +import { SplitInBatchesV1 } from './v1/SplitInBatchesV1.node'; +import { SplitInBatchesV2 } from './v2/SplitInBatchesV2.node'; + +export class SplitInBatches extends VersionedNodeType { + constructor() { + const baseDescription: INodeTypeBaseDescription = { + displayName: 'Split In Batches', + name: 'splitInBatches', + icon: 'fa:th-large', + group: ['organization'], + description: 'Split data into batches and iterate over each batch', + defaultVersion: 2, + }; + + const nodeVersions: IVersionedNodeType['nodeVersions'] = { + 1: new SplitInBatchesV1(), + 2: new SplitInBatchesV2(), + }; + + super(nodeVersions, baseDescription); } } diff --git a/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.combineData.json b/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.combineData.json index 2971f99e29ff6..94349deb7fd93 100644 --- a/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.combineData.json +++ b/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.combineData.json @@ -26,7 +26,7 @@ }, "name": "SplitInBatches1", "type": "n8n-nodes-base.splitInBatches", - "typeVersion": 1, + "typeVersion": 2, "position": [1340, 400], "id": "02d51797-ae62-4fd6-b703-426a4b3fb951" }, diff --git a/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow.json b/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v1.json similarity index 100% rename from packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow.json rename to packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v1.json diff --git a/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v2.json b/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v2.json new file mode 100644 index 0000000000000..a39dd9f41dbfb --- /dev/null +++ b/packages/nodes-base/nodes/SplitInBatches/test/SplitInBatches.workflow_v2.json @@ -0,0 +1,178 @@ +{ + "name": "Split in Batches Test", + "nodes": [ + { + "parameters": {}, + "id": "86b8149f-b0a0-489c-bb62-e59142988996", + "name": "When clicking \"Execute Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [400, 220] + }, + { + "parameters": { + "batchSize": 1, + "options": {} + }, + "id": "30c5546e-bdcc-44ff-bfca-89c5fb97b678", + "name": "Split In Batches", + "type": "n8n-nodes-base.splitInBatches", + "typeVersion": 2, + "position": [1100, 220] + }, + { + "parameters": { + "values": { + "string": [ + { + "name": "data[0]", + "value": "n8n" + }, + { + "name": "data[1]", + "value": "test" + } + ] + }, + "options": {} + }, + "id": "92d386b8-60be-4f8b-801c-b6459ec206f7", + "name": "Set", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [640, 220] + }, + { + "parameters": { + "fieldToSplitOut": "data", + "options": {} + }, + "id": "74b7e63e-a9f8-4a82-9e1f-7b2429d9118d", + "name": "Item Lists", + "type": "n8n-nodes-base.itemLists", + "typeVersion": 1, + "position": [860, 220] + }, + { + "parameters": { + "conditions": { + "boolean": [ + { + "value1": "={{ $node[\"Split In Batches\"].context[\"noItemsLeft\"] }}", + "value2": true + } + ] + } + }, + "id": "a5f68369-4e70-4f16-b260-3c8b74517993", + "name": "IF", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [1280, 220] + }, + { + "parameters": { + "values": { + "string": [ + { + "name": "maxRunIndex", + "value": "={{ $node[\"Split In Batches\"].context[\"maxRunIndex\"] }}" + }, + { + "value": "={{ $node[\"Split In Batches\"].context[\"currentRunIndex\"] }}" + } + ] + }, + "options": {} + }, + "id": "1f44eb0a-5fb7-43a7-8281-84a8a7ec8464", + "name": "Output", + "type": "n8n-nodes-base.set", + "typeVersion": 1, + "position": [1480, 200] + } + ], + "pinData": { + "Output": [ + { + "json": { + "data": "test", + "maxRunIndex": 2, + "propertyName": 1 + } + } + ] + }, + "connections": { + "When clicking \"Execute Workflow\"": { + "main": [ + [ + { + "node": "Set", + "type": "main", + "index": 0 + } + ] + ] + }, + "Split In Batches": { + "main": [ + [ + { + "node": "IF", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set": { + "main": [ + [ + { + "node": "Item Lists", + "type": "main", + "index": 0 + } + ] + ] + }, + "Item Lists": { + "main": [ + [ + { + "node": "Split In Batches", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF": { + "main": [ + [ + { + "node": "Output", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Split In Batches", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": {}, + "versionId": "de1a454e-43e9-4c2d-b786-18da5d97940f", + "id": "389", + "meta": { + "instanceId": "REMOVED" + }, + "tags": [] +} diff --git a/packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts b/packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts new file mode 100644 index 0000000000000..ba7d2c497abfd --- /dev/null +++ b/packages/nodes-base/nodes/SplitInBatches/v1/SplitInBatchesV1.node.ts @@ -0,0 +1,143 @@ +/* eslint-disable n8n-nodes-base/node-filename-against-convention */ +import type { + IExecuteFunctions, + INodeExecutionData, + INodeType, + INodeTypeDescription, + IPairedItemData, +} from 'n8n-workflow'; +import { deepCopy } from 'n8n-workflow'; + +export class SplitInBatchesV1 implements INodeType { + description: INodeTypeDescription = { + displayName: 'Split In Batches', + name: 'splitInBatches', + icon: 'fa:th-large', + group: ['organization'], + version: 1, + description: 'Split data into batches and iterate over each batch', + defaults: { + name: 'Split In Batches', + color: '#007755', + }, + inputs: ['main'], + outputs: ['main'], + properties: [ + { + displayName: + 'You may not need this node — n8n nodes automatically run once for each input item. More info', + name: 'splitInBatchesNotice', + type: 'notice', + default: '', + }, + { + displayName: 'Batch Size', + name: 'batchSize', + type: 'number', + typeOptions: { + minValue: 1, + }, + default: 10, + description: 'The number of items to return with each call', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + options: [ + { + displayName: 'Reset', + name: 'reset', + type: 'boolean', + default: false, + description: + 'Whether the node will be reset and so with the current input-data newly initialized', + }, + ], + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + // Get the input data and create a new array so that we can remove + // items without a problem + const items = this.getInputData().slice(); + + const nodeContext = this.getContext('node'); + + const batchSize = this.getNodeParameter('batchSize', 0) as number; + + const returnItems: INodeExecutionData[] = []; + + const options = this.getNodeParameter('options', 0, {}); + + if (nodeContext.items === undefined || options.reset === true) { + // Is the first time the node runs + + const sourceData = this.getInputSourceData(); + + nodeContext.currentRunIndex = 0; + nodeContext.maxRunIndex = Math.ceil(items.length / batchSize); + nodeContext.sourceData = deepCopy(sourceData); + + // Get the items which should be returned + returnItems.push.apply(returnItems, items.splice(0, batchSize)); + + // Set the other items to be saved in the context to return at later runs + nodeContext.items = [...items]; + } else { + // The node has been called before. So return the next batch of items. + nodeContext.currentRunIndex += 1; + returnItems.push.apply( + returnItems, + (nodeContext.items as INodeExecutionData[]).splice(0, batchSize), + ); + + const addSourceOverwrite = (pairedItem: IPairedItemData | number): IPairedItemData => { + if (typeof pairedItem === 'number') { + return { + item: pairedItem, + sourceOverwrite: nodeContext.sourceData, + }; + } + + return { + ...pairedItem, + sourceOverwrite: nodeContext.sourceData, + }; + }; + + function getPairedItemInformation( + item: INodeExecutionData, + ): IPairedItemData | IPairedItemData[] { + if (item.pairedItem === undefined) { + return { + item: 0, + sourceOverwrite: nodeContext.sourceData, + }; + } + + if (Array.isArray(item.pairedItem)) { + return item.pairedItem.map(addSourceOverwrite); + } + + return addSourceOverwrite(item.pairedItem); + } + + returnItems.map((item) => { + item.pairedItem = getPairedItemInformation(item); + }); + } + + nodeContext.noItemsLeft = nodeContext.items.length === 0; + + if (returnItems.length === 0) { + // No data left to return so stop execution of the branch + return null; + } + + return [returnItems]; + } +} diff --git a/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts b/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts new file mode 100644 index 0000000000000..31252fd48384a --- /dev/null +++ b/packages/nodes-base/nodes/SplitInBatches/v2/SplitInBatchesV2.node.ts @@ -0,0 +1,161 @@ +/* eslint-disable n8n-nodes-base/node-filename-against-convention */ +import type { + IExecuteFunctions, + INodeExecutionData, + INodeType, + INodeTypeDescription, + IPairedItemData, +} from 'n8n-workflow'; +import { deepCopy } from 'n8n-workflow'; + +export class SplitInBatchesV2 implements INodeType { + description: INodeTypeDescription = { + displayName: 'Split In Batches', + name: 'splitInBatches', + icon: 'fa:th-large', + group: ['organization'], + version: 2, + description: 'Split data into batches and iterate over each batch', + defaults: { + name: 'Split In Batches', + color: '#007755', + }, + inputs: ['main'], + // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong + outputs: ['main', 'main'], + outputNames: ['loop', 'done'], + properties: [ + { + displayName: + 'You may not need this node — n8n nodes automatically run once for each input item. More info', + name: 'splitInBatchesNotice', + type: 'notice', + default: '', + }, + { + displayName: 'Batch Size', + name: 'batchSize', + type: 'number', + typeOptions: { + minValue: 1, + }, + default: 10, + description: 'The number of items to return with each call', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + options: [ + { + displayName: 'Reset', + name: 'reset', + type: 'boolean', + default: false, + description: + 'Whether the node will be reset and so with the current input-data newly initialized', + }, + ], + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + // Get the input data and create a new array so that we can remove + // items without a problem + const items = this.getInputData().slice(); + + const nodeContext = this.getContext('node'); + + const batchSize = this.getNodeParameter('batchSize', 0) as number; + + const returnItems: INodeExecutionData[] = []; + + const options = this.getNodeParameter('options', 0, {}); + + if (nodeContext.items === undefined || options.reset === true) { + // Is the first time the node runs + + const sourceData = this.getInputSourceData(); + + nodeContext.currentRunIndex = 0; + nodeContext.maxRunIndex = Math.ceil(items.length / batchSize); + nodeContext.sourceData = deepCopy(sourceData); + + // Get the items which should be returned + returnItems.push.apply(returnItems, items.splice(0, batchSize)); + + // Save the incoming items to be able to return them for later runs + nodeContext.items = [...items]; + + // Reset processedItems as they get only added starting from the first iteration + nodeContext.processedItems = []; + } else { + // The node has been called before. So return the next batch of items. + nodeContext.currentRunIndex += 1; + returnItems.push.apply( + returnItems, + (nodeContext.items as INodeExecutionData[]).splice(0, batchSize), + ); + + const addSourceOverwrite = (pairedItem: IPairedItemData | number): IPairedItemData => { + if (typeof pairedItem === 'number') { + return { + item: pairedItem, + sourceOverwrite: nodeContext.sourceData, + }; + } + + return { + ...pairedItem, + sourceOverwrite: nodeContext.sourceData, + }; + }; + + function getPairedItemInformation( + item: INodeExecutionData, + ): IPairedItemData | IPairedItemData[] { + if (item.pairedItem === undefined) { + return { + item: 0, + sourceOverwrite: nodeContext.sourceData, + }; + } + + if (Array.isArray(item.pairedItem)) { + return item.pairedItem.map(addSourceOverwrite); + } + + return addSourceOverwrite(item.pairedItem); + } + + const sourceOverwrite = this.getInputSourceData(); + + const newItems = items.map((item, index) => { + return { + ...item, + pairedItem: { + sourceOverwrite, + item: index, + }, + }; + }); + + nodeContext.processedItems = [...nodeContext.processedItems, ...newItems]; + + returnItems.map((item) => { + item.pairedItem = getPairedItemInformation(item); + }); + } + + nodeContext.noItemsLeft = nodeContext.items.length === 0; + + if (returnItems.length === 0) { + return [[], nodeContext.processedItems]; + } + + return [returnItems, []]; + } +} From a70e639c75302597564e07fb410c7dc0fb0066da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Wed, 22 Mar 2023 14:22:50 +0100 Subject: [PATCH 2/9] fix(core): Require Auth on icons and nodes/credentials types static files (no-changelog) (#5745) --- packages/cli/src/Server.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 92a7632b2ea32..e9c9d6acc4bd4 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -435,8 +435,6 @@ class Server extends AbstractServer { 'assets', 'healthz', 'metrics', - 'icons', - 'types', 'e2e', this.endpointWebhook, this.endpointWebhookTest, From 4ac944af3028b70ae600000300c16de77c1af1d5 Mon Sep 17 00:00:00 2001 From: agobrech <45268029+agobrech@users.noreply.github.com> Date: Wed, 22 Mar 2023 22:04:25 +0100 Subject: [PATCH 3/9] fix(HTTP Request Node): Fix AWS credentials to automatically deconstruct the url (#5751) Workaround to use decompose uri whe OptionsUri is being used --- .../nodes-base/credentials/Aws.credentials.ts | 81 ++++++++++++------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/packages/nodes-base/credentials/Aws.credentials.ts b/packages/nodes-base/credentials/Aws.credentials.ts index 46d126872ebeb..9e28902ce3702 100644 --- a/packages/nodes-base/credentials/Aws.credentials.ts +++ b/packages/nodes-base/credentials/Aws.credentials.ts @@ -9,6 +9,7 @@ import type { IHttpRequestOptions, INodeProperties, } from 'n8n-workflow'; +import type { OptionsWithUri } from 'request'; export const regions = [ { @@ -285,44 +286,62 @@ export class Aws implements ICredentialType { let body = requestOptions.body; let region = credentials.region; const query = requestOptions.qs?.query as IDataObject; - if (!requestOptions.baseURL && !requestOptions.url) { - let endpointString: string; - if (service === 'lambda' && credentials.lambdaEndpoint) { - endpointString = credentials.lambdaEndpoint as string; - } else if (service === 'sns' && credentials.snsEndpoint) { - endpointString = credentials.snsEndpoint as string; - } else if (service === 'sqs' && credentials.sqsEndpoint) { - endpointString = credentials.sqsEndpoint as string; - } else if (service === 's3' && credentials.s3Endpoint) { - endpointString = credentials.s3Endpoint as string; - } else if (service === 'ses' && credentials.sesEndpoint) { - endpointString = credentials.sesEndpoint as string; - } else if (service === 'rekognition' && credentials.rekognitionEndpoint) { - endpointString = credentials.rekognitionEndpoint as string; - } else if (service === 'sqs' && credentials.sqsEndpoint) { - endpointString = credentials.sqsEndpoint as string; - } else if (service) { - endpointString = `https://${service}.${credentials.region}.amazonaws.com`; - } - endpoint = new URL( - endpointString!.replace('{region}', credentials.region as string) + (path as string), - ); - } else { - // If no endpoint is set, we try to decompose the path and use the default endpoint - const customUrl = new URL(`${requestOptions.baseURL!}${requestOptions.url}${path ?? ''}`); - service = customUrl.hostname.split('.')[0]; - region = customUrl.hostname.split('.')[1]; + + // ! Workaround as we still use the OptionsWithUri interface which uses uri instead of url + // ! To change when we replace the interface with IHttpRequestOptions + const requestWithUri = requestOptions as unknown as OptionsWithUri; + if (requestWithUri.uri) { + requestOptions.url = requestWithUri.uri as string; + endpoint = new URL(requestOptions.url); if (service === 'sts') { try { - customUrl.searchParams.set('Action', 'GetCallerIdentity'); - customUrl.searchParams.set('Version', '2011-06-15'); + endpoint.searchParams.set('Action', 'GetCallerIdentity'); + endpoint.searchParams.set('Version', '2011-06-15'); } catch (err) { console.log(err); } } - endpoint = customUrl; + service = endpoint.hostname.split('.')[0]; + region = endpoint.hostname.split('.')[1]; + } else { + if (!requestOptions.baseURL && !requestOptions.url) { + let endpointString: string; + if (service === 'lambda' && credentials.lambdaEndpoint) { + endpointString = credentials.lambdaEndpoint as string; + } else if (service === 'sns' && credentials.snsEndpoint) { + endpointString = credentials.snsEndpoint as string; + } else if (service === 'sqs' && credentials.sqsEndpoint) { + endpointString = credentials.sqsEndpoint as string; + } else if (service === 's3' && credentials.s3Endpoint) { + endpointString = credentials.s3Endpoint as string; + } else if (service === 'ses' && credentials.sesEndpoint) { + endpointString = credentials.sesEndpoint as string; + } else if (service === 'rekognition' && credentials.rekognitionEndpoint) { + endpointString = credentials.rekognitionEndpoint as string; + } else if (service === 'sqs' && credentials.sqsEndpoint) { + endpointString = credentials.sqsEndpoint as string; + } else if (service) { + endpointString = `https://${service}.${credentials.region}.amazonaws.com`; + } + endpoint = new URL( + endpointString!.replace('{region}', credentials.region as string) + (path as string), + ); + } else { + // If no endpoint is set, we try to decompose the path and use the default endpoint + const customUrl = new URL(`${requestOptions.baseURL!}${requestOptions.url}${path ?? ''}`); + service = customUrl.hostname.split('.')[0]; + region = customUrl.hostname.split('.')[1]; + if (service === 'sts') { + try { + customUrl.searchParams.set('Action', 'GetCallerIdentity'); + customUrl.searchParams.set('Version', '2011-06-15'); + } catch (err) { + console.log(err); + } + } + endpoint = customUrl; + } } - if (query && Object.keys(query).length !== 0) { Object.keys(query).forEach((key) => { endpoint.searchParams.append(key, query[key] as string); From b0cfd69f2bf462b1e99fd05574fd1b8d7745a487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 23 Mar 2023 15:11:18 +0100 Subject: [PATCH 4/9] fix(core): Setup nodeHelpers that aren't exposed in the code sandbox (no-changelog) (#5753) --- packages/core/src/NodeExecuteFunctions.ts | 15 +++++++++++++-- packages/nodes-base/nodes/Ftp/Ftp.node.ts | 4 ++-- packages/nodes-base/nodes/Ssh/Ssh.node.ts | 2 +- packages/nodes-base/nodes/Wait/Wait.node.ts | 4 ++-- packages/nodes-base/nodes/Webhook/Webhook.node.ts | 4 ++-- packages/workflow/src/Interfaces.ts | 8 +++++++- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 44a694de8ffb9..b95b05bebd15a 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -49,6 +49,7 @@ import type { IPairedItemData, ICredentialTestFunctions, BinaryHelperFunctions, + NodeHelperFunctions, RequestHelperFunctions, FunctionsBase, IExecuteFunctions, @@ -2054,6 +2055,13 @@ const getFileSystemHelperFunctions = (node: INode): FileSystemHelperFunctions => }, }); +const getNodeHelperFunctions = ({ + executionId, +}: IWorkflowExecuteAdditionalData): NodeHelperFunctions => ({ + copyBinaryFile: async (filePath, fileName, mimeType) => + copyBinaryFile(executionId!, filePath, fileName, mimeType), +}); + const getBinaryHelperFunctions = ({ executionId, }: IWorkflowExecuteAdditionalData): BinaryHelperFunctions => ({ @@ -2064,8 +2072,9 @@ const getBinaryHelperFunctions = ({ prepareBinaryData(binaryData, executionId!, filePath, mimeType), setBinaryDataBuffer: async (data, binaryData) => setBinaryDataBuffer(data, binaryData, executionId!), - copyBinaryFile: async (filePath, fileName, mimeType) => - copyBinaryFile(executionId!, filePath, fileName, mimeType), + copyBinaryFile: async () => { + throw new Error('copyBinaryFile has been removed. Please upgrade this node'); + }, }); /** @@ -2368,6 +2377,7 @@ export function getExecuteFunctions( normalizeItems, constructExecutionMetaData, }, + nodeHelpers: getNodeHelperFunctions(additionalData), }; })(workflow, runExecutionData, connectionInputData, inputData, node) as IExecuteFunctions; } @@ -2758,6 +2768,7 @@ export function getExecuteWebhookFunctions( ...getBinaryHelperFunctions(additionalData), returnJsonArray, }, + nodeHelpers: getNodeHelperFunctions(additionalData), }; })(workflow, node); } diff --git a/packages/nodes-base/nodes/Ftp/Ftp.node.ts b/packages/nodes-base/nodes/Ftp/Ftp.node.ts index 66575b454b7cd..a50b15f538df9 100644 --- a/packages/nodes-base/nodes/Ftp/Ftp.node.ts +++ b/packages/nodes-base/nodes/Ftp/Ftp.node.ts @@ -603,7 +603,7 @@ export class Ftp implements INodeType { const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i); const filePathDownload = this.getNodeParameter('path', i) as string; - items[i].binary![dataPropertyNameDownload] = await this.helpers.copyBinaryFile( + items[i].binary![dataPropertyNameDownload] = await this.nodeHelpers.copyBinaryFile( binaryFile.path, filePathDownload, ); @@ -699,7 +699,7 @@ export class Ftp implements INodeType { const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i); const filePathDownload = this.getNodeParameter('path', i) as string; - items[i].binary![dataPropertyNameDownload] = await this.helpers.copyBinaryFile( + items[i].binary![dataPropertyNameDownload] = await this.nodeHelpers.copyBinaryFile( binaryFile.path, filePathDownload, ); diff --git a/packages/nodes-base/nodes/Ssh/Ssh.node.ts b/packages/nodes-base/nodes/Ssh/Ssh.node.ts index 04fb96fcc1505..b39d429374d31 100644 --- a/packages/nodes-base/nodes/Ssh/Ssh.node.ts +++ b/packages/nodes-base/nodes/Ssh/Ssh.node.ts @@ -329,7 +329,7 @@ export class Ssh implements INodeType { items[i] = newItem; - items[i].binary![dataPropertyNameDownload] = await this.helpers.copyBinaryFile( + items[i].binary![dataPropertyNameDownload] = await this.nodeHelpers.copyBinaryFile( path, parameterPath, ); diff --git a/packages/nodes-base/nodes/Wait/Wait.node.ts b/packages/nodes-base/nodes/Wait/Wait.node.ts index 72a60bbbcf1f6..a6eff82ad7133 100644 --- a/packages/nodes-base/nodes/Wait/Wait.node.ts +++ b/packages/nodes-base/nodes/Wait/Wait.node.ts @@ -714,7 +714,7 @@ export class Wait implements INodeType { } const fileJson = file.toJSON(); - returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( + returnItem.binary![binaryPropertyName] = await this.nodeHelpers.copyBinaryFile( file.path, fileJson.name || fileJson.filename, fileJson.type as string, @@ -747,7 +747,7 @@ export class Wait implements INodeType { }; const binaryPropertyName = (options.binaryPropertyName || 'data') as string; - returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( + returnItem.binary![binaryPropertyName] = await this.nodeHelpers.copyBinaryFile( binaryFile.path, mimeType, ); diff --git a/packages/nodes-base/nodes/Webhook/Webhook.node.ts b/packages/nodes-base/nodes/Webhook/Webhook.node.ts index 716fa35b8582e..f2fa74f7b645d 100644 --- a/packages/nodes-base/nodes/Webhook/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook/Webhook.node.ts @@ -526,7 +526,7 @@ export class Webhook implements INodeType { } const fileJson = file.toJSON(); - returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( + returnItem.binary![binaryPropertyName] = await this.nodeHelpers.copyBinaryFile( file.path, fileJson.name || fileJson.filename, fileJson.type as string, @@ -559,7 +559,7 @@ export class Webhook implements INodeType { }; const binaryPropertyName = (options.binaryPropertyName || 'data') as string; - returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( + returnItem.binary![binaryPropertyName] = await this.nodeHelpers.copyBinaryFile( binaryFile.path, mimeType, ); diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index b0faaa05ceb58..6770237c5fa16 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -676,12 +676,16 @@ export interface BinaryHelperFunctions { mimeType?: string, ): Promise; setBinaryDataBuffer(data: IBinaryData, binaryData: Buffer): Promise; - copyBinaryFile(filePath: string, fileName: string, mimeType?: string): Promise; + copyBinaryFile(): Promise; binaryToBuffer(body: Buffer | Readable): Promise; getBinaryStream(binaryDataId: string, chunkSize?: number): Readable; getBinaryMetadata(binaryDataId: string): Promise; } +export interface NodeHelperFunctions { + copyBinaryFile(filePath: string, fileName: string, mimeType?: string): Promise; +} + export interface RequestHelperFunctions { request(uriOrObject: string | IDataObject | any, options?: IDataObject): Promise; requestWithAuthentication( @@ -753,6 +757,7 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn & sendMessageToUI(message: any): void; sendResponse(response: IExecuteResponsePromiseData): void; + nodeHelpers: NodeHelperFunctions; helpers: RequestHelperFunctions & BaseHelperFunctions & BinaryHelperFunctions & @@ -875,6 +880,7 @@ export interface IWebhookFunctions extends FunctionsBaseWithRequiredKeys<'getMod outputData: INodeExecutionData[], outputIndex?: number, ): Promise; + nodeHelpers: NodeHelperFunctions; helpers: RequestHelperFunctions & BaseHelperFunctions & BinaryHelperFunctions & From 57748b71e5cd1399ccaedb9a115b821b34cf55e5 Mon Sep 17 00:00:00 2001 From: Michael Auerswald Date: Thu, 23 Mar 2023 15:12:19 +0100 Subject: [PATCH 5/9] feat(core): Limit user invites when SAML is enabled (#5761) limit user invites when saml is enabled --- packages/cli/src/controllers/users.controller.ts | 14 ++++++++------ .../cli/src/middlewares/userManagementEnabled.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/middlewares/userManagementEnabled.ts diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index ddb8a5c6cceea..bb094f9ef1841 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -13,7 +13,6 @@ import { getInstanceBaseUrl, hashPassword, isEmailSetUp, - isUserManagementEnabled, sanitizeUser, validatePassword, withFeatureFlags, @@ -35,6 +34,8 @@ import type { import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { AuthIdentity } from '@db/entities/AuthIdentity'; import type { PostHogClient } from '@/posthog'; +import { userManagementEnabledMiddleware } from '../middlewares/userManagementEnabled'; +import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers'; @RestController('/users') export class UsersController { @@ -98,14 +99,15 @@ export class UsersController { /** * Send email invite(s) to one or multiple users and create user shell(s). */ - @Post('/') + @Post('/', { middlewares: [userManagementEnabledMiddleware] }) async sendEmailInvites(req: UserRequest.Invite) { - // TODO: this should be checked in the middleware rather than here - if (!isUserManagementEnabled()) { + if (isSamlLicensedAndEnabled()) { this.logger.debug( - 'Request to send email invite(s) to user(s) failed because user management is disabled', + 'SAML is enabled, so users are managed by the Identity Provider and cannot be added through invites', + ); + throw new BadRequestError( + 'SAML is enabled, so users are managed by the Identity Provider and cannot be added through invites', ); - throw new BadRequestError('User management is disabled'); } if (!this.config.getEnv('userManagement.isInstanceOwnerSetUp')) { diff --git a/packages/cli/src/middlewares/userManagementEnabled.ts b/packages/cli/src/middlewares/userManagementEnabled.ts new file mode 100644 index 0000000000000..c1f3c58c6f35d --- /dev/null +++ b/packages/cli/src/middlewares/userManagementEnabled.ts @@ -0,0 +1,12 @@ +import type { RequestHandler } from 'express'; +import { LoggerProxy } from 'n8n-workflow'; +import { isUserManagementEnabled } from '../UserManagement/UserManagementHelper'; + +export const userManagementEnabledMiddleware: RequestHandler = (req, res, next) => { + if (isUserManagementEnabled()) { + next(); + } else { + LoggerProxy.debug('Request failed because user management is disabled'); + res.status(400).json({ status: 'error', message: 'User management is disabled' }); + } +}; From 4498c6013dc5b4646b1e3fdba3adef42bfc87952 Mon Sep 17 00:00:00 2001 From: Michael Auerswald Date: Thu, 23 Mar 2023 15:13:05 +0100 Subject: [PATCH 6/9] fix(core): Persist CurrentAuthenticationMethod setting change (#5762) * limit user invites when saml is enabled * persist CurrentAuthenticationMethod --- packages/cli/src/sso/saml/saml.service.ee.ts | 2 +- packages/cli/src/sso/saml/samlHelpers.ts | 6 +++--- packages/cli/src/sso/ssoHelpers.ts | 9 ++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/sso/saml/saml.service.ee.ts b/packages/cli/src/sso/saml/saml.service.ee.ts index 69c4db0bdb2ee..1bf8e36b9ffc2 100644 --- a/packages/cli/src/sso/saml/saml.service.ee.ts +++ b/packages/cli/src/sso/saml/saml.service.ee.ts @@ -210,7 +210,7 @@ export class SamlService { } this._samlPreferences.metadata = prefs.metadata; } - setSamlLoginEnabled(prefs.loginEnabled ?? isSamlLoginEnabled()); + await setSamlLoginEnabled(prefs.loginEnabled ?? isSamlLoginEnabled()); setSamlLoginLabel(prefs.loginLabel ?? getSamlLoginLabel()); this.getIdentityProviderInstance(true); const result = await this.saveSamlPreferencesToDb(); diff --git a/packages/cli/src/sso/saml/samlHelpers.ts b/packages/cli/src/sso/saml/samlHelpers.ts index ed2e4f3746712..ffa6132f1f40a 100644 --- a/packages/cli/src/sso/saml/samlHelpers.ts +++ b/packages/cli/src/sso/saml/samlHelpers.ts @@ -28,15 +28,15 @@ export function getSamlLoginLabel(): string { } // can only toggle between email and saml, not directly to e.g. ldap -export function setSamlLoginEnabled(enabled: boolean): void { +export async function setSamlLoginEnabled(enabled: boolean): Promise { if (enabled) { if (isEmailCurrentAuthenticationMethod()) { config.set(SAML_LOGIN_ENABLED, true); - setCurrentAuthenticationMethod('saml'); + await setCurrentAuthenticationMethod('saml'); } } else { config.set(SAML_LOGIN_ENABLED, false); - setCurrentAuthenticationMethod('email'); + await setCurrentAuthenticationMethod('email'); } } diff --git a/packages/cli/src/sso/ssoHelpers.ts b/packages/cli/src/sso/ssoHelpers.ts index bf87952818c01..dcd5c1ac3e3a9 100644 --- a/packages/cli/src/sso/ssoHelpers.ts +++ b/packages/cli/src/sso/ssoHelpers.ts @@ -1,4 +1,5 @@ import config from '@/config'; +import * as Db from '@/Db'; import type { AuthProviderType } from '@/databases/entities/AuthIdentity'; export function isSamlCurrentAuthenticationMethod(): boolean { @@ -17,6 +18,12 @@ export function doRedirectUsersFromLoginToSsoFlow(): boolean { return config.getEnv('sso.redirectLoginToSso'); } -export function setCurrentAuthenticationMethod(authenticationMethod: AuthProviderType): void { +export async function setCurrentAuthenticationMethod( + authenticationMethod: AuthProviderType, +): Promise { config.set('userManagement.authenticationMethod', authenticationMethod); + await Db.collections.Settings.save({ + key: 'userManagement.authenticationMethod', + value: authenticationMethod, + }); } From 5dd92c6b9490b02243ab2eeedee4b06006555460 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:37:49 +0100 Subject: [PATCH 7/9] :rocket: Release 0.221.0 (#5726) --- CHANGELOG.md | 36 +++++++++++++++++++++++++++++ package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/design-system/package.json | 2 +- packages/editor-ui/package.json | 2 +- packages/node-dev/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 9 files changed, 44 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 294ce6ecc0cf8..f2aa55521a13e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# [0.221.0](https://github.com/n8n-io/n8n/compare/n8n@0.220.0...n8n@0.221.0) (2023-03-23) + + +### Bug Fixes + +* **core:** Fix calling error workflows in main mode recovery ([#5698](https://github.com/n8n-io/n8n/issues/5698)) ([e0ea97a](https://github.com/n8n-io/n8n/commit/e0ea97af8d7aaa014680f5f9d5702d1cafd49757)) +* **core:** Fix telemetry execution status for manual workflows executions ([#5712](https://github.com/n8n-io/n8n/issues/5712)) ([a28396e](https://github.com/n8n-io/n8n/commit/a28396ee91bfbccc6596812606c237a8e2c34088)) +* **core:** Return saml attributes after connection test ([#5717](https://github.com/n8n-io/n8n/issues/5717)) ([be172cb](https://github.com/n8n-io/n8n/commit/be172cb720c8a44ebd1f0b86dddab321e1e3c9fd)) +* **editor:** Disable tooltip for display modes that don't support mapping ([#5715](https://github.com/n8n-io/n8n/issues/5715)) ([fb8755e](https://github.com/n8n-io/n8n/commit/fb8755ea3c720c98f002a6756c39b8fed11482c0)) +* **editor:** Fix execution list item selection ([#5606](https://github.com/n8n-io/n8n/issues/5606)) ([7a352ef](https://github.com/n8n-io/n8n/commit/7a352efff944c52062412e53ea2c1a034a25f908)) +* **editor:** Fix for large notifications being cut off ([#5705](https://github.com/n8n-io/n8n/issues/5705)) ([c07f838](https://github.com/n8n-io/n8n/commit/c07f838ce60dc33261fe3e1b5e7dd6fe05f1d63b)) +* **editor:** Fix redo in code and expression editor ([#5708](https://github.com/n8n-io/n8n/issues/5708)) ([cd7a55b](https://github.com/n8n-io/n8n/commit/cd7a55ba5aeb83d1e540a65b5c6b2c74fd742461)) +* **editor:** Fix the canvas node distance when automatically injecting manual trigger ([#5716](https://github.com/n8n-io/n8n/issues/5716)) ([cb2ba97](https://github.com/n8n-io/n8n/commit/cb2ba97f3837b572e237da1256b9f2ee376767a9)) +* **HTTP Request Node:** Fix AWS credentials to automatically deconstruct the url ([#5751](https://github.com/n8n-io/n8n/issues/5751)) ([4ac944a](https://github.com/n8n-io/n8n/commit/4ac944af3028b70ae600000300c16de77c1af1d5)) +* **Split In Batches Node:** Roll back changes in v1 and create v2 ([#5747](https://github.com/n8n-io/n8n/issues/5747)) ([cefec77](https://github.com/n8n-io/n8n/commit/cefec7739b6da820d64f9476476e1901d4f386bf)) + + +### Features + +* **core:** Augment data instead of copying it ([#5487](https://github.com/n8n-io/n8n/issues/5487)) ([0876c38](https://github.com/n8n-io/n8n/commit/0876c38aaeb8355141fecbc14e84cdda0b2c737b)) +* **editor:** Move canvas by holding Space or Middle mouse button ([#5719](https://github.com/n8n-io/n8n/issues/5719)) ([19dded1](https://github.com/n8n-io/n8n/commit/19dded18c9a588a30b9ac1fc274dcd967e9b7b6b)) +* **editor:** Recommend and pre-select auth type with overrides ([#5684](https://github.com/n8n-io/n8n/issues/5684)) ([f59b591](https://github.com/n8n-io/n8n/commit/f59b591c93ecd7cbd279668abe6494ef2b88c831)) +* **editor:** SSO login button ([#5615](https://github.com/n8n-io/n8n/issues/5615)) ([6916628](https://github.com/n8n-io/n8n/commit/6916628a9f11e07cbcdf390f747f396fb0ef9e3c)) +* **QuickChart Node:** Add QuickChart node ([#3572](https://github.com/n8n-io/n8n/issues/3572)) ([233f1fa](https://github.com/n8n-io/n8n/commit/233f1fa7ec230e92e868de0247e315aa6a705ead)) + + + +## [0.220.1](https://github.com/n8n-io/n8n/compare/n8n@0.220.0...n8n@0.220.1) (2023-03-22) + + +### Bug Fixes + +* **Split In Batches Node:** Roll back changes in v1 and create v2 ([#5747](https://github.com/n8n-io/n8n/issues/5747)) ([6d1c88e](https://github.com/n8n-io/n8n/commit/6d1c88ea8c2e5dc72c6e6edeeeef52dc1fba4075)) + + + # [0.220.0](https://github.com/n8n-io/n8n/compare/n8n@0.219.1...n8n@0.220.0) (2023-03-16) diff --git a/package.json b/package.json index e69c7a29867e7..830e762f8eb31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.220.0", + "version": "0.221.0", "private": true, "homepage": "https://n8n.io", "engines": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 83feb4ce8e53f..c0649065ec4f6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.220.0", + "version": "0.221.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/core/package.json b/packages/core/package.json index 472e4ec1f6757..a1ea182a0ce00 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.159.0", + "version": "0.160.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 40d9464dd8b19..bb56ef9560ea0 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "0.58.0", + "version": "0.59.0", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", "author": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 6823adcc849d3..5bf7f2ad54bec 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.186.0", + "version": "0.187.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 432be1663b9d2..2b0759927c27b 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "0.98.0", + "version": "0.99.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index d638228998dca..30a3ec5b88205 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.218.0", + "version": "0.219.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index ae585d7d988b6..d257335d47ccf 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.141.0", + "version": "0.142.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 573bc8bceac5ee3ac923780cd29c51c7e5fd9187 Mon Sep 17 00:00:00 2001 From: Cornelius Suermann Date: Thu, 23 Mar 2023 15:38:01 +0100 Subject: [PATCH 8/9] docs: Refer to docker.n8n.io/n8nio/n8n for pulling n8n docker images (no-changelog) (#5763) --- .../subfolderWithSSL/docker-compose.yml | 2 +- docker/compose/withMariaDB/docker-compose.yml | 2 +- .../compose/withPostgres/docker-compose.yml | 2 +- .../withPostgresAndWorker/docker-compose.yml | 4 +- docker/images/n8n/README.md | 127 +++++++++--------- packages/cli/README.md | 6 +- 6 files changed, 73 insertions(+), 70 deletions(-) diff --git a/docker/compose/subfolderWithSSL/docker-compose.yml b/docker/compose/subfolderWithSSL/docker-compose.yml index f6ce218bff0c7..37a4b25f182f2 100644 --- a/docker/compose/subfolderWithSSL/docker-compose.yml +++ b/docker/compose/subfolderWithSSL/docker-compose.yml @@ -20,7 +20,7 @@ services: - ${DATA_FOLDER}/letsencrypt:/letsencrypt - /var/run/docker.sock:/var/run/docker.sock:ro n8n: - image: n8nio/n8n + image: docker.n8n.io/n8nio/n8n ports: - '127.0.0.1:5678:5678' labels: diff --git a/docker/compose/withMariaDB/docker-compose.yml b/docker/compose/withMariaDB/docker-compose.yml index 14e478683eae1..e7a1db74e0ff9 100644 --- a/docker/compose/withMariaDB/docker-compose.yml +++ b/docker/compose/withMariaDB/docker-compose.yml @@ -23,7 +23,7 @@ services: retries: 10 n8n: - image: n8nio/n8n + image: docker.n8n.io/n8nio/n8n restart: always environment: - DB_TYPE=mariadb diff --git a/docker/compose/withPostgres/docker-compose.yml b/docker/compose/withPostgres/docker-compose.yml index c102d81837261..9b3ab5b83e23f 100644 --- a/docker/compose/withPostgres/docker-compose.yml +++ b/docker/compose/withPostgres/docker-compose.yml @@ -24,7 +24,7 @@ services: retries: 10 n8n: - image: n8nio/n8n + image: docker.n8n.io/n8nio/n8n restart: always environment: - DB_TYPE=postgresdb diff --git a/docker/compose/withPostgresAndWorker/docker-compose.yml b/docker/compose/withPostgresAndWorker/docker-compose.yml index 308e5abc47fc1..9f947d735d49b 100644 --- a/docker/compose/withPostgresAndWorker/docker-compose.yml +++ b/docker/compose/withPostgresAndWorker/docker-compose.yml @@ -63,14 +63,14 @@ services: n8n: <<: *shared - image: n8nio/n8n + image: docker.n8n.io/n8nio/n8n command: /bin/sh -c "n8n start --tunnel" ports: - 5678:5678 n8n-worker: <<: *shared - image: n8nio/n8n + image: docker.n8n.io/n8nio/n8n command: /bin/sh -c "sleep 5; n8n worker" depends_on: - n8n diff --git a/docker/images/n8n/README.md b/docker/images/n8n/README.md index 4cec9c4cc24a2..376c4494abb68 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -40,12 +40,12 @@ Additional information and example workflows on the n8n.io website: [https://n8n ## Start n8n in Docker -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n + --name n8n \ + -p 5678:5678 \ + -v ~/.n8n:/home/node/.n8n \ + docker.n8n.io/n8nio/n8n ``` You can then access n8n by opening: @@ -62,13 +62,13 @@ n8n instance. To use it simply start n8n with `--tunnel` -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n \ - n8n start --tunnel + --name n8n \ + -p 5678:5678 \ + -v ~/.n8n:/home/node/.n8n \ + docker.n8n.io/n8nio/n8n \ + n8n start --tunnel ``` ## Securing n8n @@ -79,7 +79,7 @@ to make sure that n8n is protected! Right now we have very basic protection via basic-auth in place. It can be activated by setting the following environment variables: -``` +```text N8N_BASIC_AUTH_ACTIVE=true N8N_BASIC_AUTH_USER= N8N_BASIC_AUTH_PASSWORD= @@ -91,12 +91,12 @@ The workflow data gets by default saved in an SQLite database in the user folder (`/home/node/.n8n`). That folder also additionally contains the settings like webhook URL and encryption key. -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n + --name n8n \ + -p 5678:5678 \ + -v ~/.n8n:/home/node/.n8n \ + docker.n8n.io/n8nio/n8n ``` ### Start with other Database @@ -123,20 +123,20 @@ Replace the following placeholders with the actual data: - POSTGRES_USER - POSTGRES_SCHEMA -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -e DB_TYPE=postgresdb \ - -e DB_POSTGRESDB_DATABASE= \ - -e DB_POSTGRESDB_HOST= \ - -e DB_POSTGRESDB_PORT= \ - -e DB_POSTGRESDB_USER= \ - -e DB_POSTGRESDB_SCHEMA= \ - -e DB_POSTGRESDB_PASSWORD= \ - -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n \ - n8n start + --name n8n \ + -p 5678:5678 \ + -e DB_TYPE=postgresdb \ + -e DB_POSTGRESDB_DATABASE= \ + -e DB_POSTGRESDB_HOST= \ + -e DB_POSTGRESDB_PORT= \ + -e DB_POSTGRESDB_USER= \ + -e DB_POSTGRESDB_SCHEMA= \ + -e DB_POSTGRESDB_PASSWORD= \ + -v ~/.n8n:/home/node/.n8n \ + docker.n8n.io/n8nio/n8n \ + n8n start ``` A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withPostgres/README.md) @@ -151,19 +151,19 @@ Replace the following placeholders with the actual data: - MYSQLDB_PORT - MYSQLDB_USER -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -e DB_TYPE=mysqldb \ - -e DB_MYSQLDB_DATABASE= \ - -e DB_MYSQLDB_HOST= \ - -e DB_MYSQLDB_PORT= \ - -e DB_MYSQLDB_USER= \ - -e DB_MYSQLDB_PASSWORD= \ - -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n \ - n8n start + --name n8n \ + -p 5678:5678 \ + -e DB_TYPE=mysqldb \ + -e DB_MYSQLDB_DATABASE= \ + -e DB_MYSQLDB_HOST= \ + -e DB_MYSQLDB_PORT= \ + -e DB_MYSQLDB_USER= \ + -e DB_MYSQLDB_PASSWORD= \ + -v ~/.n8n:/home/node/.n8n \ + docker.n8n.io/n8nio/n8n \ + n8n start ``` ## Passing Sensitive Data via File @@ -191,16 +191,21 @@ A basic step by step example setup of n8n with docker-compose and Lets Encrypt i ## Updating a running docker-compose instance -``` -# Pull down the latest version from dockerhub -docker pull n8nio/n8n -# Stop current setup -sudo docker-compose stop -# Delete it (will only delete the docker-containers, data is stored separately) -sudo docker-compose rm -# Then start it again -sudo docker-compose up -d -``` +1. Pull the latest version from the registry + + `docker pull docker.n8n.io/n8nio/n8n` + +2. Stop the current setup + + `sudo docker-compose stop` + +3. Delete it (will only delete the docker-containers, data is stored separately) + + `sudo docker-compose rm` + +4. Then start it again + + `sudo docker-compose up -d` ## Setting Timezone @@ -212,22 +217,22 @@ the environment variable `TZ`. Example to use the same timezone for both: -``` +```bash docker run -it --rm \ - --name n8n \ - -p 5678:5678 \ - -e GENERIC_TIMEZONE="Europe/Berlin" \ - -e TZ="Europe/Berlin" \ - n8nio/n8n + --name n8n \ + -p 5678:5678 \ + -e GENERIC_TIMEZONE="Europe/Berlin" \ + -e TZ="Europe/Berlin" \ + docker.n8n.io/n8nio/n8n ``` ## Build Docker-Image -``` -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg N8N_VERSION= -t n8nio/n8n: . +```bash +docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg N8N_VERSION= -t n8n: . # For example: -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg N8N_VERSION=0.114.0 -t n8nio/n8n:0.114.0 . +docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg N8N_VERSION=0.114.0 -t n8n:0.114.0 . ``` ## What does n8n mean and how do you pronounce it? diff --git a/packages/cli/README.md b/packages/cli/README.md index b7043db9150ae..6641c7d12adb9 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -58,7 +58,7 @@ To play around with n8n, you can also start it using Docker: docker run -it --rm \ --name n8n \ -p 5678:5678 \ - n8nio/n8n + docker.n8n.io/n8nio/n8n ``` Be aware that all the data will be lost once the Docker container gets removed. To persist the data mount the `~/.n8n` folder: @@ -68,11 +68,9 @@ docker run -it --rm \ --name n8n \ -p 5678:5678 \ -v ~/.n8n:/home/node/.n8n \ - n8nio/n8n + docker.n8n.io/n8nio/n8n ``` -n8n also offers a Docker image for Raspberry Pi: `n8nio/n8n:latest-rpi`. - Refer to the [documentation](https://github.com/n8n-io/n8n/blob/master/docker/images/n8n/README.md) for more information on the Docker setup. ### Install with npm From 4c583e2be4ed23dfa3c32492db6d7f9fea64b413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 23 Mar 2023 16:54:35 +0100 Subject: [PATCH 9/9] ci: Fix saml.api.test.ts (no-changelog) (#5764) --- packages/cli/src/sso/ssoHelpers.ts | 1 + .../cli/test/integration/saml/saml.api.test.ts | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/sso/ssoHelpers.ts b/packages/cli/src/sso/ssoHelpers.ts index dcd5c1ac3e3a9..99cf43cfd85fc 100644 --- a/packages/cli/src/sso/ssoHelpers.ts +++ b/packages/cli/src/sso/ssoHelpers.ts @@ -25,5 +25,6 @@ export async function setCurrentAuthenticationMethod( await Db.collections.Settings.save({ key: 'userManagement.authenticationMethod', value: authenticationMethod, + loadOnStartup: true, }); } diff --git a/packages/cli/test/integration/saml/saml.api.test.ts b/packages/cli/test/integration/saml/saml.api.test.ts index 3eda945f9e2a1..32b5f7fa6392a 100644 --- a/packages/cli/test/integration/saml/saml.api.test.ts +++ b/packages/cli/test/integration/saml/saml.api.test.ts @@ -10,9 +10,8 @@ import * as utils from '../shared/utils'; let owner: User; let authOwnerAgent: SuperAgentTest; -function enableSaml(enable: boolean) { - setSamlLoginEnabled(enable); - setCurrentAuthenticationMethod(enable ? 'saml' : 'email'); +async function enableSaml(enable: boolean) { + await setSamlLoginEnabled(enable); config.set('enterprise.features.saml', enable); } @@ -22,10 +21,6 @@ beforeAll(async () => { authOwnerAgent = utils.createAuthAgent(app)(owner); }); -// beforeEach(async () => { -// await testDb.truncate(['User']); -// }); - afterAll(async () => { await testDb.terminate(); }); @@ -33,7 +28,7 @@ afterAll(async () => { describe('Instance owner', () => { describe('PATCH /me', () => { test('should succeed with valid inputs', async () => { - enableSaml(false); + await enableSaml(false); await authOwnerAgent .patch('/me') .send({ @@ -46,7 +41,7 @@ describe('Instance owner', () => { }); test('should throw BadRequestError if email is changed when SAML is enabled', async () => { - enableSaml(true); + await enableSaml(true); await authOwnerAgent .patch('/me') .send({ @@ -60,7 +55,7 @@ describe('Instance owner', () => { describe('PATCH /password', () => { test('should throw BadRequestError if password is changed when SAML is enabled', async () => { - enableSaml(true); + await enableSaml(true); await authOwnerAgent .patch('/me/password') .send({