From c6aa73cc2f4598737e332f489db1a3a6abbbb81e Mon Sep 17 00:00:00 2001
From: Chris Lonardo <clonardo@gmail.com>
Date: Sun, 5 Apr 2020 21:06:00 -0400
Subject: [PATCH 1/3] upgrade mongodb client to support mongodb 4.2 and allow
 conn string override

---
 package.json                                  |  38 +--
 packages/cli/package.json                     |   2 +-
 packages/editor-ui/src/main.ts                |   2 +-
 .../nodes-base/nodes/MongoDb/MongoDb.node.ts  | 245 ++++--------------
 .../nodes/MongoDb/mongo.node.options.ts       | 156 +++++++++++
 .../nodes/MongoDb/mongo.node.utils.ts         |  47 ++++
 packages/nodes-base/package.json              |   6 +-
 7 files changed, 279 insertions(+), 217 deletions(-)
 create mode 100644 packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
 create mode 100644 packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts

diff --git a/package.json b/package.json
index b045830f51..e14010e7a8 100644
--- a/package.json
+++ b/package.json
@@ -1,20 +1,22 @@
 {
-  "name": "n8n",
-  "private": true,
-  "homepage": "https://n8n.io",
-  "scripts": {
-    "bootstrap": "lerna bootstrap --hoist --no-ci",
-    "build": "lerna exec npm run build",
-    "dev": "lerna exec npm run dev --parallel",
-    "start": "run-script-os",
-    "start:default": "cd packages/cli/bin && ./n8n",
-    "start:windows": "cd packages/cli/bin && n8n",
-    "test": "lerna run test",
-    "watch": "lerna run --parallel watch"
-  },
-  "devDependencies": {
-    "lerna": "^3.13.1",
-    "run-script-os": "^1.0.7"
-  },
-  "postcss": {}
+	"name": "n8n",
+	"private": true,
+	"homepage": "https://n8n.io",
+	"scripts": {
+		"bootstrap": "lerna bootstrap --hoist --no-ci",
+		"build": "lerna exec npm run build",
+		"dev": "lerna exec npm run dev --parallel",
+		"clean:dist": "lerna exec -- rimraf ./dist",
+		"start": "run-script-os",
+		"start:default": "cd packages/cli/bin && ./n8n",
+		"start:windows": "cd packages/cli/bin && n8n",
+		"test": "lerna run test",
+		"watch": "lerna run --parallel watch"
+	},
+	"devDependencies": {
+		"lerna": "^3.13.1",
+		"rimraf": "^3.0.2",
+		"run-script-os": "^1.0.7"
+	},
+	"postcss": {}
 }
diff --git a/packages/cli/package.json b/packages/cli/package.json
index aabfbb7f7a..386fce5dc1 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -93,7 +93,7 @@
         "jwks-rsa": "^1.6.0",
         "localtunnel": "^2.0.0",
         "lodash.get": "^4.4.2",
-        "mongodb": "^3.2.3",
+        "mongodb": "^3.5.5",
         "mysql2": "^2.0.1",
         "n8n-core": "~0.29.0",
         "n8n-editor-ui": "~0.40.0",
diff --git a/packages/editor-ui/src/main.ts b/packages/editor-ui/src/main.ts
index f9a7f879a0..e1b30cd166 100644
--- a/packages/editor-ui/src/main.ts
+++ b/packages/editor-ui/src/main.ts
@@ -179,7 +179,7 @@ if (process.env.NODE_ENV !== 'production') {
 			// not do anything about it anyway
 			return;
 		}
-		console.error('error cought in main.ts'); // eslint-disable-line no-console
+		console.error('error caught in main.ts'); // eslint-disable-line no-console
 		console.error(message); // eslint-disable-line no-console
 		console.error(error); // eslint-disable-line no-console
 	};
diff --git a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
index 0aa6d603ac..d15fd0112e 100644
--- a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
+++ b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
@@ -1,227 +1,85 @@
-import { IExecuteFunctions } from 'n8n-core';
+import { IExecuteFunctions } from "n8n-core";
 import {
 	IDataObject,
 	INodeExecutionData,
 	INodeType,
 	INodeTypeDescription,
-} from 'n8n-workflow';
-
-import { MongoClient } from 'mongodb';
-
-
-/**
- * Returns of copy of the items which only contains the json data and
- * of that only the define properties
- *
- * @param {INodeExecutionData[]} items The items to copy
- * @param {string[]} properties The properties it should include
- * @returns
- */
-function getItemCopy(items: INodeExecutionData[], properties: string[]): IDataObject[] {
-	// Prepare the data to insert and copy it to be returned
-	let newItem: IDataObject;
-	return items.map((item) => {
-		newItem = {};
-		for (const property of properties) {
-			if (item.json[property] === undefined) {
-				newItem[property] = null;
-			} else {
-				newItem[property] = JSON.parse(JSON.stringify(item.json[property]));
-			}
-		}
-		return newItem;
-	});
-}
-
+} from "n8n-workflow";
+import { nodeDescription } from "./mongo.node.options";
+import { MongoClient } from "mongodb";
+import { getItemCopy, buildParameterizedConnString } from "./mongo.node.utils";
 
 export class MongoDb implements INodeType {
-	description: INodeTypeDescription = {
-		displayName: 'MongoDB',
-		name: 'mongoDb',
-		icon: 'file:mongoDb.png',
-		group: ['input'],
-		version: 1,
-		description: 'Find, insert and update documents in MongoDB.',
-		defaults: {
-			name: 'MongoDB',
-			color: '#13AA52',
-		},
-		inputs: ['main'],
-		outputs: ['main'],
-		credentials: [
-			{
-				name: 'mongoDb',
-				required: true,
-			}
-		],
-		properties: [
-			{
-				displayName: 'Operation',
-				name: 'operation',
-				type: 'options',
-				options: [
-					{
-						name: 'Find',
-						value: 'find',
-						description: 'Find documents.',
-					},
-					{
-						name: 'Insert',
-						value: 'insert',
-						description: 'Insert documents.',
-					},
-					{
-						name: 'Update',
-						value: 'update',
-						description: 'Updates documents.',
-					},
-				],
-				default: 'find',
-				description: 'The operation to perform.',
-			},
-
-			{
-				displayName: 'Collection',
-				name: 'collection',
-				type: 'string',
-				required: true,
-				default: '',
-				description: 'MongoDB Collection'
-			},
-
-			// ----------------------------------
-			//         find
-			// ----------------------------------
-			{
-				displayName: 'Query (JSON format)',
-				name: 'query',
-				type: 'string',
-				typeOptions: {
-					rows: 5,
-				},
-				displayOptions: {
-					show: {
-						operation: [
-							'find'
-						],
-					},
-				},
-				default: '{}',
-				placeholder: `{ "birth": { "$gt": "1950-01-01" } }`,
-				required: true,
-				description: 'MongoDB Find query.',
-			},
-
-
-			// ----------------------------------
-			//         insert
-			// ----------------------------------
-			{
-				displayName: 'Fields',
-				name: 'fields',
-				type: 'string',
-				displayOptions: {
-					show: {
-						operation: [
-							'insert'
-						],
-					},
-				},
-				default: '',
-				placeholder: 'name,description',
-				description: 'Comma separated list of the fields to be included into the new document.',
-			},
-
-
-			// ----------------------------------
-			//         update
-			// ----------------------------------
-			{
-				displayName: 'Update Key',
-				name: 'updateKey',
-				type: 'string',
-				displayOptions: {
-					show: {
-						operation: [
-							'update'
-						],
-					},
-				},
-				default: 'id',
-				required: true,
-				description: 'Name of the property which decides which rows in the database should be updated. Normally that would be "id".',
-			},
-			{
-				displayName: 'Fields',
-				name: 'fields',
-				type: 'string',
-				displayOptions: {
-					show: {
-						operation: [
-							'update'
-						],
-					},
-				},
-				default: '',
-				placeholder: 'name,description',
-				description: 'Comma separated list of the fields to be included into the new document.',
-			},
-
-		]
-	};
-
+	description: INodeTypeDescription = nodeDescription;
 
 	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
+		const credentials = this.getCredentials("mongoDb");
 
-		const credentials = this.getCredentials('mongoDb');
+		// user can optionally override connection string
+		const useOverriddenConnString = this.getNodeParameter(
+			"shouldOverrideConnString",
+			0
+		) as boolean;
 
 		if (credentials === undefined) {
-			throw new Error('No credentials got returned!');
+			throw new Error("No credentials got returned!");
 		}
 
-		let connectionUri = '';
-
-		if (credentials.port) {
-			connectionUri = `mongodb://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}`;
+		let connectionUri = "";
+		if (useOverriddenConnString === true) {
+			const connStringInput = this.getNodeParameter(
+				"connStringOverrideVal",
+				0
+			) as string;
+			if (connStringInput && connStringInput.length > 0) {
+				connectionUri = connStringInput;
+			} else {
+				throw new Error(
+					"Cannot override credentials: valid MongoDB connection string not provided "
+				);
+			}
 		} else {
-			connectionUri = `mongodb+srv://${credentials.user}:${credentials.password}@${credentials.host}`;
+			connectionUri = buildParameterizedConnString(credentials);
 		}
 
-		const client = await MongoClient.connect(connectionUri, { useNewUrlParser: true, useUnifiedTopology: true });
+		const client: MongoClient = await MongoClient.connect(connectionUri, {
+			useNewUrlParser: true,
+			useUnifiedTopology: true,
+		});
+
 		const mdb = client.db(credentials.database as string);
 
 		let returnItems = [];
 
 		const items = this.getInputData();
-		const operation = this.getNodeParameter('operation', 0) as string;
+		const operation = this.getNodeParameter("operation", 0) as string;
 
-		if (operation === 'find') {
+		if (operation === "find") {
 			// ----------------------------------
 			//         find
 			// ----------------------------------
 
 			const queryResult = await mdb
-				.collection(this.getNodeParameter('collection', 0) as string)
-				.find(JSON.parse(this.getNodeParameter('query', 0) as string))
+				.collection(this.getNodeParameter("collection", 0) as string)
+				.find(JSON.parse(this.getNodeParameter("query", 0) as string))
 				.toArray();
 
 			returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]);
-
-		} else if (operation === 'insert') {
+		} else if (operation === "insert") {
 			// ----------------------------------
 			//         insert
 			// ----------------------------------
 
 			// Prepare the data to insert and copy it to be returned
-			const fields = (this.getNodeParameter('fields', 0) as string)
-				.split(',')
-				.map(f => f.trim())
-				.filter(f => !!f);
+			const fields = (this.getNodeParameter("fields", 0) as string)
+				.split(",")
+				.map((f) => f.trim())
+				.filter((f) => !!f);
 
 			const insertItems = getItemCopy(items, fields);
 
 			const { insertedIds } = await mdb
-				.collection(this.getNodeParameter('collection', 0) as string)
+				.collection(this.getNodeParameter("collection", 0) as string)
 				.insertMany(insertItems);
 
 			// Add the id to the data
@@ -230,20 +88,20 @@ export class MongoDb implements INodeType {
 					json: {
 						...insertItems[parseInt(i, 10)],
 						id: insertedIds[parseInt(i, 10)] as string,
-					}
+					},
 				});
 			}
-		} else if (operation === 'update') {
+		} else if (operation === "update") {
 			// ----------------------------------
 			//         update
 			// ----------------------------------
 
-			const fields = (this.getNodeParameter('fields', 0) as string)
-				.split(',')
-				.map(f => f.trim())
-				.filter(f => !!f);
+			const fields = (this.getNodeParameter("fields", 0) as string)
+				.split(",")
+				.map((f) => f.trim())
+				.filter((f) => !!f);
 
-			let updateKey = this.getNodeParameter('updateKey', 0) as string;
+			let updateKey = this.getNodeParameter("updateKey", 0) as string;
 			updateKey = updateKey.trim();
 
 			if (!fields.includes(updateKey)) {
@@ -258,16 +116,15 @@ export class MongoDb implements INodeType {
 					continue;
 				}
 
-				const filter: { [key: string] :string } = {};
+				const filter: { [key: string]: string } = {};
 				filter[updateKey] = item[updateKey] as string;
 
 				await mdb
-					.collection(this.getNodeParameter('collection', 0) as string)
+					.collection(this.getNodeParameter("collection", 0) as string)
 					.updateOne(filter, { $set: item });
 			}
 
 			returnItems = this.helpers.returnJsonArray(updateItems as IDataObject[]);
-
 		} else {
 			throw new Error(`The operation "${operation}" is not supported!`);
 		}
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
new file mode 100644
index 0000000000..19f18f4ab7
--- /dev/null
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
@@ -0,0 +1,156 @@
+import { INodeTypeDescription } from "n8n-workflow";
+
+/**
+ * Options to be displayed
+ */
+export const nodeDescription: INodeTypeDescription = {
+	displayName: "MongoDB",
+	name: "mongoDb",
+	icon: "file:mongoDb.png",
+	group: ["input"],
+	version: 1,
+	description: "Find, insert and update documents in MongoDB.",
+	defaults: {
+		name: "MongoDB",
+		color: "#13AA52",
+	},
+	inputs: ["main"],
+	outputs: ["main"],
+	credentials: [
+		{
+			name: "mongoDb",
+			required: true,
+		},
+	],
+	properties: [
+		{
+			displayName: "Override conn string",
+			name: "shouldOverrideConnString",
+			type: "boolean",
+			default: false,
+			description:
+				"Whether to override the generated connection string. Credentials will also be ignored in this case.",
+		},
+		{
+			displayName: "Conn string",
+			name: "connStringOverrideVal",
+			type: "string",
+			typeOptions: {
+				rows: 1,
+			},
+			displayOptions: {
+				show: {
+					shouldOverrideConnString: [true],
+				},
+			},
+			default: "",
+			placeholder: `mongodb://USERNAMEHERE:PASSWORDHERE@localhost:27017/?authSource=admin&readPreference=primary&appname=n8n&ssl=false`,
+			required: false,
+			description: `If "Override conn string" is checked, the value here will be used as a MongoDB connection string, and the MongoDB credentials will be ignored`,
+		},
+		{
+			displayName: "Operation",
+			name: "operation",
+			type: "options",
+			options: [
+				{
+					name: "Find",
+					value: "find",
+					description: "Find documents.",
+				},
+				{
+					name: "Insert",
+					value: "insert",
+					description: "Insert documents.",
+				},
+				{
+					name: "Update",
+					value: "update",
+					description: "Updates documents.",
+				},
+			],
+			default: "find",
+			description: "The operation to perform.",
+		},
+
+		{
+			displayName: "Collection",
+			name: "collection",
+			type: "string",
+			required: true,
+			default: "",
+			description: "MongoDB Collection",
+		},
+
+		// ----------------------------------
+		//         find
+		// ----------------------------------
+		{
+			displayName: "Query (JSON format)",
+			name: "query",
+			type: "string",
+			typeOptions: {
+				rows: 5,
+			},
+			displayOptions: {
+				show: {
+					operation: ["find"],
+				},
+			},
+			default: "{}",
+			placeholder: `{ "birth": { "$gt": "1950-01-01" } }`,
+			required: true,
+			description: "MongoDB Find query.",
+		},
+
+		// ----------------------------------
+		//         insert
+		// ----------------------------------
+		{
+			displayName: "Fields",
+			name: "fields",
+			type: "string",
+			displayOptions: {
+				show: {
+					operation: ["insert"],
+				},
+			},
+			default: "",
+			placeholder: "name,description",
+			description:
+				"Comma separated list of the fields to be included into the new document.",
+		},
+
+		// ----------------------------------
+		//         update
+		// ----------------------------------
+		{
+			displayName: "Update Key",
+			name: "updateKey",
+			type: "string",
+			displayOptions: {
+				show: {
+					operation: ["update"],
+				},
+			},
+			default: "id",
+			required: true,
+			description:
+				'Name of the property which decides which rows in the database should be updated. Normally that would be "id".',
+		},
+		{
+			displayName: "Fields",
+			name: "fields",
+			type: "string",
+			displayOptions: {
+				show: {
+					operation: ["update"],
+				},
+			},
+			default: "",
+			placeholder: "name,description",
+			description:
+				"Comma separated list of the fields to be included into the new document.",
+		},
+	],
+};
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
new file mode 100644
index 0000000000..7de725f08a
--- /dev/null
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
@@ -0,0 +1,47 @@
+import {
+	IDataObject,
+	INodeExecutionData,
+	ICredentialDataDecryptedObject,
+} from "n8n-workflow";
+
+/**
+ * Standard way of building the MongoDB connection string, unless overridden with a provided string
+ *
+ * @param {ICredentialDataDecryptedObject} credentials MongoDB credentials to use, unless conn string is overridden
+ */
+export function buildParameterizedConnString(
+	credentials: ICredentialDataDecryptedObject
+): string {
+	if (credentials.port) {
+		return `mongodb://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}`;
+	} else {
+		return `mongodb+srv://${credentials.user}:${credentials.password}@${credentials.host}`;
+	}
+}
+
+/**
+ * Returns of copy of the items which only contains the json data and
+ * of that only the define properties
+ *
+ * @param {INodeExecutionData[]} items The items to copy
+ * @param {string[]} properties The properties it should include
+ * @returns
+ */
+export function getItemCopy(
+	items: INodeExecutionData[],
+	properties: string[]
+): IDataObject[] {
+	// Prepare the data to insert and copy it to be returned
+	let newItem: IDataObject;
+	return items.map((item) => {
+		newItem = {};
+		for (const property of properties) {
+			if (item.json[property] === undefined) {
+				newItem[property] = null;
+			} else {
+				newItem[property] = JSON.parse(JSON.stringify(item.json[property]));
+			}
+		}
+		return newItem;
+	});
+}
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index e197bd6ca4..b1a3cbda7b 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -75,7 +75,7 @@
             "dist/credentials/MandrillApi.credentials.js",
             "dist/credentials/MattermostApi.credentials.js",
             "dist/credentials/MauticApi.credentials.js",
-			"dist/credentials/MoceanApi.credentials.js",
+            "dist/credentials/MoceanApi.credentials.js",
             "dist/credentials/MondayComApi.credentials.js",
             "dist/credentials/MongoDb.credentials.js",
             "dist/credentials/Msg91Api.credentials.js",
@@ -255,7 +255,7 @@
         "@types/jest": "^24.0.18",
         "@types/lodash.set": "^4.3.6",
         "@types/moment-timezone": "^0.5.12",
-        "@types/mongodb": "^3.3.6",
+        "@types/mongodb": "^3.5.4",
         "@types/node": "^10.10.1",
         "@types/nodemailer": "^4.6.5",
         "@types/redis": "^2.8.11",
@@ -284,7 +284,7 @@
         "lodash.get": "^4.4.2",
         "lodash.set": "^4.3.2",
         "lodash.unset": "^4.5.2",
-        "mongodb": "^3.3.2",
+        "mongodb": "^3.5.5",
         "mysql2": "^2.0.1",
         "n8n-core": "~0.29.0",
         "nodemailer": "^5.1.1",

From 00fc6e598025022edca6b9461e1d5c9caaf85060 Mon Sep 17 00:00:00 2001
From: Chris Lonardo <clonardo@gmail.com>
Date: Mon, 6 Apr 2020 14:04:59 -0400
Subject: [PATCH 2/3] enhance .editorconfig and apply formatting cleanup to
 last commit

---
 .editorconfig                                 |   7 +
 package.json                                  |  40 ++---
 .../nodes-base/nodes/MongoDb/MongoDb.node.ts  |  64 +++----
 .../nodes/MongoDb/mongo.node.options.ts       | 156 +++++++++---------
 .../nodes/MongoDb/mongo.node.utils.ts         |   6 +-
 5 files changed, 140 insertions(+), 133 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index bec7553240..5d02a5688b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,3 +6,10 @@ indent_style = tab
 end_of_line = lf
 insert_final_newline = true
 trim_trailing_whitespace = true
+
+[package.json]
+indent_style = space
+indent_size = 2
+
+[*.ts]
+quote_type = single
\ No newline at end of file
diff --git a/package.json b/package.json
index e14010e7a8..0c7d8dac15 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,22 @@
 {
-	"name": "n8n",
-	"private": true,
-	"homepage": "https://n8n.io",
-	"scripts": {
-		"bootstrap": "lerna bootstrap --hoist --no-ci",
-		"build": "lerna exec npm run build",
-		"dev": "lerna exec npm run dev --parallel",
-		"clean:dist": "lerna exec -- rimraf ./dist",
-		"start": "run-script-os",
-		"start:default": "cd packages/cli/bin && ./n8n",
-		"start:windows": "cd packages/cli/bin && n8n",
-		"test": "lerna run test",
-		"watch": "lerna run --parallel watch"
-	},
-	"devDependencies": {
-		"lerna": "^3.13.1",
-		"rimraf": "^3.0.2",
-		"run-script-os": "^1.0.7"
-	},
-	"postcss": {}
+  "name": "n8n",
+  "private": true,
+  "homepage": "https://n8n.io",
+  "scripts": {
+    "bootstrap": "lerna bootstrap --hoist --no-ci",
+    "build": "lerna exec npm run build",
+    "dev": "lerna exec npm run dev --parallel",
+    "clean:dist": "lerna exec -- rimraf ./dist",
+    "start": "run-script-os",
+    "start:default": "cd packages/cli/bin && ./n8n",
+    "start:windows": "cd packages/cli/bin && n8n",
+    "test": "lerna run test",
+    "watch": "lerna run --parallel watch"
+  },
+  "devDependencies": {
+    "lerna": "^3.13.1",
+    "rimraf": "^3.0.2",
+    "run-script-os": "^1.0.7"
+  },
+  "postcss": {}
 }
diff --git a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
index d15fd0112e..7dc8daa957 100644
--- a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
+++ b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
@@ -1,41 +1,41 @@
-import { IExecuteFunctions } from "n8n-core";
+import { IExecuteFunctions } from 'n8n-core';
 import {
 	IDataObject,
 	INodeExecutionData,
 	INodeType,
-	INodeTypeDescription,
-} from "n8n-workflow";
-import { nodeDescription } from "./mongo.node.options";
-import { MongoClient } from "mongodb";
-import { getItemCopy, buildParameterizedConnString } from "./mongo.node.utils";
+	INodeTypeDescription
+} from 'n8n-workflow';
+import { nodeDescription } from './mongo.node.options';
+import { MongoClient } from 'mongodb';
+import { getItemCopy, buildParameterizedConnString } from './mongo.node.utils';
 
 export class MongoDb implements INodeType {
 	description: INodeTypeDescription = nodeDescription;
 
 	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
-		const credentials = this.getCredentials("mongoDb");
+		const credentials = this.getCredentials('mongoDb');
 
 		// user can optionally override connection string
 		const useOverriddenConnString = this.getNodeParameter(
-			"shouldOverrideConnString",
+			'shouldOverrideConnString',
 			0
 		) as boolean;
 
 		if (credentials === undefined) {
-			throw new Error("No credentials got returned!");
+			throw new Error('No credentials got returned!');
 		}
 
-		let connectionUri = "";
+		let connectionUri = '';
 		if (useOverriddenConnString === true) {
 			const connStringInput = this.getNodeParameter(
-				"connStringOverrideVal",
+				'connStringOverrideVal',
 				0
 			) as string;
 			if (connStringInput && connStringInput.length > 0) {
 				connectionUri = connStringInput;
 			} else {
 				throw new Error(
-					"Cannot override credentials: valid MongoDB connection string not provided "
+					'Cannot override credentials: valid MongoDB connection string not provided '
 				);
 			}
 		} else {
@@ -44,7 +44,7 @@ export class MongoDb implements INodeType {
 
 		const client: MongoClient = await MongoClient.connect(connectionUri, {
 			useNewUrlParser: true,
-			useUnifiedTopology: true,
+			useUnifiedTopology: true
 		});
 
 		const mdb = client.db(credentials.database as string);
@@ -52,34 +52,34 @@ export class MongoDb implements INodeType {
 		let returnItems = [];
 
 		const items = this.getInputData();
-		const operation = this.getNodeParameter("operation", 0) as string;
+		const operation = this.getNodeParameter('operation', 0) as string;
 
-		if (operation === "find") {
+		if (operation === 'find') {
 			// ----------------------------------
 			//         find
 			// ----------------------------------
 
 			const queryResult = await mdb
-				.collection(this.getNodeParameter("collection", 0) as string)
-				.find(JSON.parse(this.getNodeParameter("query", 0) as string))
+				.collection(this.getNodeParameter('collection', 0) as string)
+				.find(JSON.parse(this.getNodeParameter('query', 0) as string))
 				.toArray();
 
 			returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]);
-		} else if (operation === "insert") {
+		} else if (operation === 'insert') {
 			// ----------------------------------
 			//         insert
 			// ----------------------------------
 
 			// Prepare the data to insert and copy it to be returned
-			const fields = (this.getNodeParameter("fields", 0) as string)
-				.split(",")
-				.map((f) => f.trim())
-				.filter((f) => !!f);
+			const fields = (this.getNodeParameter('fields', 0) as string)
+				.split(',')
+				.map(f => f.trim())
+				.filter(f => !!f);
 
 			const insertItems = getItemCopy(items, fields);
 
 			const { insertedIds } = await mdb
-				.collection(this.getNodeParameter("collection", 0) as string)
+				.collection(this.getNodeParameter('collection', 0) as string)
 				.insertMany(insertItems);
 
 			// Add the id to the data
@@ -87,21 +87,21 @@ export class MongoDb implements INodeType {
 				returnItems.push({
 					json: {
 						...insertItems[parseInt(i, 10)],
-						id: insertedIds[parseInt(i, 10)] as string,
-					},
+						id: insertedIds[parseInt(i, 10)] as string
+					}
 				});
 			}
-		} else if (operation === "update") {
+		} else if (operation === 'update') {
 			// ----------------------------------
 			//         update
 			// ----------------------------------
 
-			const fields = (this.getNodeParameter("fields", 0) as string)
-				.split(",")
-				.map((f) => f.trim())
-				.filter((f) => !!f);
+			const fields = (this.getNodeParameter('fields', 0) as string)
+				.split(',')
+				.map(f => f.trim())
+				.filter(f => !!f);
 
-			let updateKey = this.getNodeParameter("updateKey", 0) as string;
+			let updateKey = this.getNodeParameter('updateKey', 0) as string;
 			updateKey = updateKey.trim();
 
 			if (!fields.includes(updateKey)) {
@@ -120,7 +120,7 @@ export class MongoDb implements INodeType {
 				filter[updateKey] = item[updateKey] as string;
 
 				await mdb
-					.collection(this.getNodeParameter("collection", 0) as string)
+					.collection(this.getNodeParameter('collection', 0) as string)
 					.updateOne(filter, { $set: item });
 			}
 
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
index 19f18f4ab7..4a98eeabd5 100644
--- a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
@@ -1,156 +1,156 @@
-import { INodeTypeDescription } from "n8n-workflow";
+import { INodeTypeDescription } from 'n8n-workflow';
 
 /**
  * Options to be displayed
  */
 export const nodeDescription: INodeTypeDescription = {
-	displayName: "MongoDB",
-	name: "mongoDb",
-	icon: "file:mongoDb.png",
-	group: ["input"],
+	displayName: 'MongoDB',
+	name: 'mongoDb',
+	icon: 'file:mongoDb.png',
+	group: ['input'],
 	version: 1,
-	description: "Find, insert and update documents in MongoDB.",
+	description: 'Find, insert and update documents in MongoDB.',
 	defaults: {
-		name: "MongoDB",
-		color: "#13AA52",
+		name: 'MongoDB',
+		color: '#13AA52'
 	},
-	inputs: ["main"],
-	outputs: ["main"],
+	inputs: ['main'],
+	outputs: ['main'],
 	credentials: [
 		{
-			name: "mongoDb",
-			required: true,
-		},
+			name: 'mongoDb',
+			required: true
+		}
 	],
 	properties: [
 		{
-			displayName: "Override conn string",
-			name: "shouldOverrideConnString",
-			type: "boolean",
+			displayName: 'Override conn string',
+			name: 'shouldOverrideConnString',
+			type: 'boolean',
 			default: false,
 			description:
-				"Whether to override the generated connection string. Credentials will also be ignored in this case.",
+				'Whether to override the generated connection string. Credentials will also be ignored in this case.'
 		},
 		{
-			displayName: "Conn string",
-			name: "connStringOverrideVal",
-			type: "string",
+			displayName: 'Conn string',
+			name: 'connStringOverrideVal',
+			type: 'string',
 			typeOptions: {
-				rows: 1,
+				rows: 1
 			},
 			displayOptions: {
 				show: {
-					shouldOverrideConnString: [true],
-				},
+					shouldOverrideConnString: [true]
+				}
 			},
-			default: "",
+			default: '',
 			placeholder: `mongodb://USERNAMEHERE:PASSWORDHERE@localhost:27017/?authSource=admin&readPreference=primary&appname=n8n&ssl=false`,
 			required: false,
-			description: `If "Override conn string" is checked, the value here will be used as a MongoDB connection string, and the MongoDB credentials will be ignored`,
+			description: `If "Override conn string" is checked, the value here will be used as a MongoDB connection string, and the MongoDB credentials will be ignored`
 		},
 		{
-			displayName: "Operation",
-			name: "operation",
-			type: "options",
+			displayName: 'Operation',
+			name: 'operation',
+			type: 'options',
 			options: [
 				{
-					name: "Find",
-					value: "find",
-					description: "Find documents.",
+					name: 'Find',
+					value: 'find',
+					description: 'Find documents.'
 				},
 				{
-					name: "Insert",
-					value: "insert",
-					description: "Insert documents.",
+					name: 'Insert',
+					value: 'insert',
+					description: 'Insert documents.'
 				},
 				{
-					name: "Update",
-					value: "update",
-					description: "Updates documents.",
-				},
+					name: 'Update',
+					value: 'update',
+					description: 'Updates documents.'
+				}
 			],
-			default: "find",
-			description: "The operation to perform.",
+			default: 'find',
+			description: 'The operation to perform.'
 		},
 
 		{
-			displayName: "Collection",
-			name: "collection",
-			type: "string",
+			displayName: 'Collection',
+			name: 'collection',
+			type: 'string',
 			required: true,
-			default: "",
-			description: "MongoDB Collection",
+			default: '',
+			description: 'MongoDB Collection'
 		},
 
 		// ----------------------------------
 		//         find
 		// ----------------------------------
 		{
-			displayName: "Query (JSON format)",
-			name: "query",
-			type: "string",
+			displayName: 'Query (JSON format)',
+			name: 'query',
+			type: 'string',
 			typeOptions: {
-				rows: 5,
+				rows: 5
 			},
 			displayOptions: {
 				show: {
-					operation: ["find"],
-				},
+					operation: ['find']
+				}
 			},
-			default: "{}",
+			default: '{}',
 			placeholder: `{ "birth": { "$gt": "1950-01-01" } }`,
 			required: true,
-			description: "MongoDB Find query.",
+			description: 'MongoDB Find query.'
 		},
 
 		// ----------------------------------
 		//         insert
 		// ----------------------------------
 		{
-			displayName: "Fields",
-			name: "fields",
-			type: "string",
+			displayName: 'Fields',
+			name: 'fields',
+			type: 'string',
 			displayOptions: {
 				show: {
-					operation: ["insert"],
-				},
+					operation: ['insert']
+				}
 			},
-			default: "",
-			placeholder: "name,description",
+			default: '',
+			placeholder: 'name,description',
 			description:
-				"Comma separated list of the fields to be included into the new document.",
+				'Comma separated list of the fields to be included into the new document.'
 		},
 
 		// ----------------------------------
 		//         update
 		// ----------------------------------
 		{
-			displayName: "Update Key",
-			name: "updateKey",
-			type: "string",
+			displayName: 'Update Key',
+			name: 'updateKey',
+			type: 'string',
 			displayOptions: {
 				show: {
-					operation: ["update"],
-				},
+					operation: ['update']
+				}
 			},
-			default: "id",
+			default: 'id',
 			required: true,
 			description:
-				'Name of the property which decides which rows in the database should be updated. Normally that would be "id".',
+				'Name of the property which decides which rows in the database should be updated. Normally that would be "id".'
 		},
 		{
-			displayName: "Fields",
-			name: "fields",
-			type: "string",
+			displayName: 'Fields',
+			name: 'fields',
+			type: 'string',
 			displayOptions: {
 				show: {
-					operation: ["update"],
-				},
+					operation: ['update']
+				}
 			},
-			default: "",
-			placeholder: "name,description",
+			default: '',
+			placeholder: 'name,description',
 			description:
-				"Comma separated list of the fields to be included into the new document.",
-		},
-	],
+				'Comma separated list of the fields to be included into the new document.'
+		}
+	]
 };
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
index 7de725f08a..54cae7d53c 100644
--- a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
@@ -1,8 +1,8 @@
 import {
 	IDataObject,
 	INodeExecutionData,
-	ICredentialDataDecryptedObject,
-} from "n8n-workflow";
+	ICredentialDataDecryptedObject
+} from 'n8n-workflow';
 
 /**
  * Standard way of building the MongoDB connection string, unless overridden with a provided string
@@ -33,7 +33,7 @@ export function getItemCopy(
 ): IDataObject[] {
 	// Prepare the data to insert and copy it to be returned
 	let newItem: IDataObject;
-	return items.map((item) => {
+	return items.map(item => {
 		newItem = {};
 		for (const property of properties) {
 			if (item.json[property] === undefined) {

From d80deda34fc8271f08256cba8c0bf5928c5691fd Mon Sep 17 00:00:00 2001
From: Chris Lonardo <clonardo@gmail.com>
Date: Mon, 6 Apr 2020 21:07:54 -0400
Subject: [PATCH 3/3] Moved MongoDb connection string override into credentials
 (#1)

* interim work on credentials refactor

* move conn string override for mongodb into credentials and enhance credentials typings
---
 .../credentials/MongoDb.credentials.ts        | 39 +++++++++---
 .../nodes-base/nodes/MongoDb/MongoDb.node.ts  | 40 +++---------
 .../nodes/MongoDb/mongo.node.options.ts       | 25 --------
 .../nodes/MongoDb/mongo.node.types.ts         | 52 ++++++++++++++++
 .../nodes/MongoDb/mongo.node.utils.ts         | 61 ++++++++++++++++++-
 5 files changed, 149 insertions(+), 68 deletions(-)
 create mode 100644 packages/nodes-base/nodes/MongoDb/mongo.node.types.ts

diff --git a/packages/nodes-base/credentials/MongoDb.credentials.ts b/packages/nodes-base/credentials/MongoDb.credentials.ts
index b39c7016de..436e9f2e4f 100644
--- a/packages/nodes-base/credentials/MongoDb.credentials.ts
+++ b/packages/nodes-base/credentials/MongoDb.credentials.ts
@@ -1,8 +1,4 @@
-import {
-	ICredentialType,
-	NodePropertyTypes,
-} from 'n8n-workflow';
-
+import { ICredentialType, NodePropertyTypes } from 'n8n-workflow';
 
 export class MongoDb implements ICredentialType {
 	name = 'mongoDb';
@@ -12,34 +8,57 @@ export class MongoDb implements ICredentialType {
 			displayName: 'Host',
 			name: 'host',
 			type: 'string' as NodePropertyTypes,
-			default: 'localhost',
+			default: 'localhost'
 		},
 		{
 			displayName: 'Database',
 			name: 'database',
 			type: 'string' as NodePropertyTypes,
 			default: '',
+			description:
+				'Note: the database should still be provided even if using an override connection string'
 		},
 		{
 			displayName: 'User',
 			name: 'user',
 			type: 'string' as NodePropertyTypes,
-			default: '',
+			default: ''
 		},
 		{
 			displayName: 'Password',
 			name: 'password',
 			type: 'string' as NodePropertyTypes,
 			typeOptions: {
-				password: true,
+				password: true
 			},
-			default: '',
+			default: ''
 		},
 		{
 			displayName: 'Port',
 			name: 'port',
 			type: 'number' as NodePropertyTypes,
-			default: 27017,
+			default: 27017
 		},
+		{
+			displayName: 'Override conn string',
+			name: 'shouldOverrideConnString',
+			type: 'boolean' as NodePropertyTypes,
+			default: false,
+			required: false,
+			description:
+				'Whether to override the generated connection string. Credentials will also be ignored in this case.'
+		},
+		{
+			displayName: 'Conn string override',
+			name: 'connStringOverrideVal',
+			type: 'string' as NodePropertyTypes,
+			typeOptions: {
+				rows: 1
+			},
+			default: '',
+			placeholder: `mongodb://USERNAMEHERE:PASSWORDHERE@localhost:27017/?authSource=admin&readPreference=primary&appname=n8n&ssl=false`,
+			required: false,
+			description: `If provided, the value here will be used as a MongoDB connection string, and the MongoDB credentials will be ignored`
+		}
 	];
 }
diff --git a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
index 7dc8daa957..dbd7ab2929 100644
--- a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
+++ b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts
@@ -7,47 +7,25 @@ import {
 } from 'n8n-workflow';
 import { nodeDescription } from './mongo.node.options';
 import { MongoClient } from 'mongodb';
-import { getItemCopy, buildParameterizedConnString } from './mongo.node.utils';
+import {
+	getItemCopy,
+	validateAndResolveMongoCredentials
+} from './mongo.node.utils';
 
 export class MongoDb implements INodeType {
 	description: INodeTypeDescription = nodeDescription;
 
 	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
-		const credentials = this.getCredentials('mongoDb');
+		const { database, connectionString } = validateAndResolveMongoCredentials(
+			this.getCredentials('mongoDb')
+		);
 
-		// user can optionally override connection string
-		const useOverriddenConnString = this.getNodeParameter(
-			'shouldOverrideConnString',
-			0
-		) as boolean;
-
-		if (credentials === undefined) {
-			throw new Error('No credentials got returned!');
-		}
-
-		let connectionUri = '';
-		if (useOverriddenConnString === true) {
-			const connStringInput = this.getNodeParameter(
-				'connStringOverrideVal',
-				0
-			) as string;
-			if (connStringInput && connStringInput.length > 0) {
-				connectionUri = connStringInput;
-			} else {
-				throw new Error(
-					'Cannot override credentials: valid MongoDB connection string not provided '
-				);
-			}
-		} else {
-			connectionUri = buildParameterizedConnString(credentials);
-		}
-
-		const client: MongoClient = await MongoClient.connect(connectionUri, {
+		const client: MongoClient = await MongoClient.connect(connectionString, {
 			useNewUrlParser: true,
 			useUnifiedTopology: true
 		});
 
-		const mdb = client.db(credentials.database as string);
+		const mdb = client.db(database as string);
 
 		let returnItems = [];
 
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
index 4a98eeabd5..190ff3de88 100644
--- a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts
@@ -23,31 +23,6 @@ export const nodeDescription: INodeTypeDescription = {
 		}
 	],
 	properties: [
-		{
-			displayName: 'Override conn string',
-			name: 'shouldOverrideConnString',
-			type: 'boolean',
-			default: false,
-			description:
-				'Whether to override the generated connection string. Credentials will also be ignored in this case.'
-		},
-		{
-			displayName: 'Conn string',
-			name: 'connStringOverrideVal',
-			type: 'string',
-			typeOptions: {
-				rows: 1
-			},
-			displayOptions: {
-				show: {
-					shouldOverrideConnString: [true]
-				}
-			},
-			default: '',
-			placeholder: `mongodb://USERNAMEHERE:PASSWORDHERE@localhost:27017/?authSource=admin&readPreference=primary&appname=n8n&ssl=false`,
-			required: false,
-			description: `If "Override conn string" is checked, the value here will be used as a MongoDB connection string, and the MongoDB credentials will be ignored`
-		},
 		{
 			displayName: 'Operation',
 			name: 'operation',
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.types.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.types.ts
new file mode 100644
index 0000000000..2995fdd2a2
--- /dev/null
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.types.ts
@@ -0,0 +1,52 @@
+import { CredentialInformation } from 'n8n-workflow';
+
+/**
+ * Credentials object for Mongo, if using individual parameters
+ */
+export interface IMongoParametricCredentials {
+	/**
+	 * Whether to allow overriding the parametric credentials with a connection string
+	 */
+	shouldOverrideConnString: false | undefined | null;
+	host: string;
+	database: string;
+	user: string;
+	password: string;
+	port?: number;
+}
+
+/**
+ * Credentials object for Mongo, if using override connection string
+ */
+export interface IMongoOverrideCredentials {
+	/**
+	 * Whether to allow overriding the parametric credentials with a connection string
+	 */
+	shouldOverrideConnString: true;
+	/**
+	 * If using an override connection string, this is where it will be.
+	 */
+	connStringOverrideVal: string;
+	database: string;
+}
+
+/**
+ * Unified credential object type (whether params are overridden with a connection string or not)
+ */
+export type IMongoCredentialsType =
+	| IMongoParametricCredentials
+	| IMongoOverrideCredentials;
+
+/**
+ * Resolve the database and connection string from input credentials
+ */
+export type IMongoCredentials = {
+	/**
+	 * Database name (used to create the Mongo client)
+	 */
+	database: string;
+	/**
+	 * Generated connection string (after validating and figuring out overrides)
+	 */
+	connectionString: string;
+};
diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
index 54cae7d53c..1d682a450a 100644
--- a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
+++ b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts
@@ -3,14 +3,19 @@ import {
 	INodeExecutionData,
 	ICredentialDataDecryptedObject
 } from 'n8n-workflow';
+import {
+	IMongoCredentialsType,
+	IMongoParametricCredentials,
+	IMongoCredentials
+} from './mongo.node.types';
 
 /**
  * Standard way of building the MongoDB connection string, unless overridden with a provided string
  *
  * @param {ICredentialDataDecryptedObject} credentials MongoDB credentials to use, unless conn string is overridden
  */
-export function buildParameterizedConnString(
-	credentials: ICredentialDataDecryptedObject
+function buildParameterizedConnString(
+	credentials: IMongoParametricCredentials
 ): string {
 	if (credentials.port) {
 		return `mongodb://${credentials.user}:${credentials.password}@${credentials.host}:${credentials.port}`;
@@ -19,6 +24,58 @@ export function buildParameterizedConnString(
 	}
 }
 
+/**
+ * Build mongoDb connection string and resolve database name.
+ * If a connection string override value is provided, that will be used in place of individual args
+ *
+ * @param {ICredentialDataDecryptedObject} credentials raw/input MongoDB credentials to use
+ */
+function buildMongoConnectionParams(
+	credentials: IMongoCredentialsType
+): IMongoCredentials {
+	const sanitizedDbName =
+		credentials.database && credentials.database.trim().length > 0
+			? credentials.database.trim()
+			: '';
+	if (credentials.shouldOverrideConnString) {
+		if (
+			credentials.connStringOverrideVal &&
+			credentials.connStringOverrideVal.trim().length > 0
+		) {
+			return {
+				connectionString: credentials.connStringOverrideVal.trim(),
+				database: sanitizedDbName
+			};
+		} else {
+			throw new Error(
+				'Cannot override credentials: valid MongoDB connection string not provided '
+			);
+		}
+	} else {
+		return {
+			connectionString: buildParameterizedConnString(credentials),
+			database: sanitizedDbName
+		};
+	}
+}
+
+/**
+ * Verify credentials. If ok, build mongoDb connection string and resolve database name.
+ *
+ * @param {ICredentialDataDecryptedObject} credentials raw/input MongoDB credentials to use
+ */
+export function validateAndResolveMongoCredentials(
+	credentials?: ICredentialDataDecryptedObject
+): IMongoCredentials {
+	if (credentials == undefined) {
+		throw new Error('No credentials got returned!');
+	} else {
+		return buildMongoConnectionParams(
+			(credentials as any) as IMongoCredentialsType
+		);
+	}
+}
+
 /**
  * Returns of copy of the items which only contains the json data and
  * of that only the define properties