From db0d00f8618a88829621a9b62064fc99e478f80d Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Mon, 11 May 2020 20:42:02 +0200 Subject: [PATCH] feat: add new connection event --- package-lock.json | 340 ++++++++++++++++-- package.json | 3 + src/connectionController.ts | 166 +++++++-- src/dataServiceType.ts | 2 + src/mdbExtensionController.ts | 3 +- src/telemetry/telemetryController.ts | 25 +- src/test/suite/connectionController.test.ts | 66 ++-- .../editors/activeDBCodeLensProvider.test.ts | 18 +- .../collectionDocumentsProvider.test.ts | 84 +++-- .../suite/explorer/explorerController.test.ts | 8 +- src/test/suite/extension.test.ts | 9 + src/test/suite/mdbExtensionController.test.ts | 32 +- .../telemetry/telemetryController.test.ts | 30 +- .../connect-form/connection-form.tsx | 9 +- 14 files changed, 653 insertions(+), 142 deletions(-) diff --git a/package-lock.json b/package-lock.json index e017271d0..a5bd074ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3435,6 +3435,15 @@ } } }, + "cross-fetch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz", + "integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==", + "requires": { + "node-fetch": "2.6.0", + "whatwg-fetch": "3.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -4358,6 +4367,14 @@ } } }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -5958,6 +5975,218 @@ } } }, + "gce-ips": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/gce-ips/-/gce-ips-1.0.2.tgz", + "integrity": "sha1-00WaCCldNCChorvHVeCw2Rz6JuY=", + "requires": { + "async": "^1.5.2", + "ip-range-check": "0.0.1", + "lodash.assign": "^4.0.8", + "yargs": "^4.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } + }, "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -6198,8 +6427,7 @@ "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "graceful-readlink": { "version": "1.0.1", @@ -6410,8 +6638,7 @@ "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", - "dev": true + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" }, "html-tokenize": { "version": "2.0.1", @@ -6575,7 +6802,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -6770,8 +6996,20 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "optional": true + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ip-range-check": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.0.1.tgz", + "integrity": "sha1-yvKXQ6xJh7qXntc70idzsY75i3o=", + "requires": { + "ipaddr.js": "^1.0.1" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -7049,6 +7287,11 @@ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "dev": true }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -7250,7 +7493,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "optional": true, "requires": { "invert-kv": "^1.0.0" } @@ -7733,8 +7975,7 @@ "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lodash.has": { "version": "3.2.1", @@ -8294,6 +8535,11 @@ "integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=", "dev": true }, + "mingo": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mingo/-/mingo-1.3.3.tgz", + "integrity": "sha1-aSLE0Ufvx3GgFCWixMj3eER4xUY=" + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -8591,6 +8837,18 @@ "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.0.0.tgz", "integrity": "sha512-ZFcBteBQMoGyMV3+FmjPRRI4X2ZR8TNPGlylNuGU1yg/TTC7eXFcd2VhL7SslRM0/tGsx7pIjLEqwjd9CtWSlA==" }, + "mongodb-cloud-info": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mongodb-cloud-info/-/mongodb-cloud-info-1.1.2.tgz", + "integrity": "sha512-i/VTOErBDCpIcPLKrHg7kUsE/+kXVjmyTgBPkTUT5xXIqrPVq/JYU5t63Hz54ZEhnDJzWQXJr1yECR100x5u5g==", + "requires": { + "cross-fetch": "^3.0.4", + "gce-ips": "^1.0.2", + "lodash.get": "^4.4.2", + "netmask": "^1.0.6", + "save": "^2.4.0" + } + }, "mongodb-collection-sample": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/mongodb-collection-sample/-/mongodb-collection-sample-4.5.1.tgz", @@ -9870,6 +10128,11 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "netmask": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", + "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -9914,6 +10177,11 @@ "semver": "^5.7.0" } }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -10020,7 +10288,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -10513,7 +10780,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "optional": true, "requires": { "lcid": "^1.0.0" } @@ -10804,14 +11070,12 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -11815,6 +12079,32 @@ "sparse-bitfield": "^3.0.3" } }, + "save": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/save/-/save-2.4.0.tgz", + "integrity": "sha512-wd5L2uVnsKYkIUaK6i8Ie66IOHaI328gMF0MPuTJtYOjXgUolC33LSIk7Qr8WVA55QHaGwfiVS8a7EFIeGOR3w==", + "requires": { + "async": "^2.6.2", + "event-stream": "^4.0.1", + "lodash.assign": "^4.2.0", + "mingo": "1" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + } + } + }, "scheduler": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", @@ -12020,6 +12310,12 @@ } } }, + "sinon-chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", + "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -12252,7 +12548,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -12261,14 +12556,12 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -12277,8 +12570,7 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "split": { "version": "1.0.1", @@ -13321,7 +13613,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -14613,6 +14904,11 @@ "source-map": "~0.6.1" } }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 95bafa8df..eaaceed46 100644 --- a/package.json +++ b/package.json @@ -604,6 +604,8 @@ "classnames": "^2.2.6", "debug": "^4.1.1", "dotenv": "^8.2.0", + "encoding": "^0.1.12", + "mongodb-cloud-info": "^1.1.2", "mongodb-connection-model": "^16.0.0", "mongodb-data-service": "^16.6.5", "mongodb-ns": "^2.2.0", @@ -660,6 +662,7 @@ "ora": "^4.0.3", "postcss-loader": "^3.0.0", "sinon": "^9.0.0", + "sinon-chai": "^3.5.0", "style-loader": "^1.1.3", "ts-loader": "^6.2.2", "ts-node": "^8.6.2", diff --git a/src/connectionController.ts b/src/connectionController.ts index ff0f59523..f4090f1ae 100644 --- a/src/connectionController.ts +++ b/src/connectionController.ts @@ -3,9 +3,6 @@ import * as vscode from 'vscode'; import Connection = require('mongodb-connection-model/lib/model'); import DataService = require('mongodb-data-service'); import * as keytarType from 'keytar'; - -const { name, version } = require('../package.json'); - import { ConnectionModelType } from './connectionModelType'; import { DataServiceType } from './dataServiceType'; import { createLogger } from './logging'; @@ -14,16 +11,29 @@ import { EventEmitter } from 'events'; import { StorageController, StorageVariables } from './storage'; import { SavedConnection, StorageScope } from './storage/storageController'; import { getNodeModule } from './utils/getNodeModule'; +import TelemetryController, { + TelemetryEventTypes +} from './telemetry/telemetryController'; +import { getCloudInfo } from 'mongodb-cloud-info'; +const { name, version } = require('../package.json'); const log = createLogger('connection controller'); const MAX_CONNECTION_NAME_LENGTH = 512; +const ATLAS_REGEX = /mongodb.net[:/]/i; +const LOCALHOST_REGEX = /(localhost|127\.0\.0\.1)/i; type KeyTar = typeof keytarType; export enum DataServiceEventTypes { CONNECTIONS_DID_CHANGE = 'CONNECTIONS_DID_CHANGE', ACTIVE_CONNECTION_CHANGED = 'ACTIVE_CONNECTION_CHANGED', - ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING', + ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING' +} + +export enum ConnectionTypes { + CONNECTION_FORM = 'CONNECTION_FORM', + CONNECTION_STRING = 'CONNECTION_STRING', + CONNECTION_ID = 'CONNECTION_ID' } export type SavedConnectionInformation = { @@ -55,13 +65,19 @@ export default class ConnectionController { private _statusView: StatusView; private _storageController: StorageController; + public _telemetryController?: TelemetryController; // Used by other parts of the extension that respond to changes in the connections. private eventEmitter: EventEmitter = new EventEmitter(); - constructor(_statusView: StatusView, storageController: StorageController) { + constructor( + _statusView: StatusView, + storageController: StorageController, + telemetryController?: TelemetryController + ) { this._statusView = _statusView; this._storageController = storageController; + this._telemetryController = telemetryController; try { // We load keytar in two different ways. This is because when the @@ -216,21 +232,21 @@ export default class ConnectionController { return reject(new Error(`Unable to create connection: ${error}`)); } - return this.saveNewConnectionAndConnect(newConnectionModel).then( - resolve, - reject - ); + return this.saveNewConnectionAndConnect( + newConnectionModel, + ConnectionTypes.CONNECTION_STRING + ).then(resolve, reject); } ); }); }; public parseNewConnectionAndConnect = ( - newConnectionModel + newConnectionModel: ConnectionModelType ): Promise => { // Here we re-parse the connection, as it can be loaded from storage or // passed by the connection model without the class methods. - let connectionModel; + let connectionModel: ConnectionModelType; try { connectionModel = new Connection(newConnectionModel); @@ -239,11 +255,15 @@ export default class ConnectionController { return Promise.reject(new Error(`Unable to load connection: ${error}`)); } - return this.saveNewConnectionAndConnect(connectionModel); + return this.saveNewConnectionAndConnect( + connectionModel, + ConnectionTypes.CONNECTION_FORM + ); }; public saveNewConnectionAndConnect = async ( - connectionModel: ConnectionModelType + connectionModel: ConnectionModelType, + connectionType: ConnectionTypes ): Promise => { const { driverUrl, instanceId } = connectionModel.getAttributes({ derived: true @@ -279,19 +299,91 @@ export default class ConnectionController { } return new Promise((resolve, reject) => { - this.connect(connectionId, connectionModel).then((connectSuccess) => { - if (!connectSuccess) { - return resolve(false); - } + this.connect(connectionId, connectionModel, connectionType).then( + (connectSuccess) => { + if (!connectSuccess) { + return resolve(false); + } - resolve(true); - }, reject); + resolve(true); + }, + reject + ); }); }; + public async getCloudInfoFromDataService(firstServerHostname) { + const cloudInfo = await getCloudInfo(firstServerHostname); + let isPublicCloud = false; + let publicCloudName: string | null = null; + + if (cloudInfo.isAws) { + isPublicCloud = true; + publicCloudName = 'aws'; + } else if (cloudInfo.isGcp) { + isPublicCloud = true; + publicCloudName = 'gcp'; + } else if (cloudInfo.isAzure) { + isPublicCloud = true; + publicCloudName = 'azure'; + } + + return { isPublicCloud, publicCloudName }; + } + + private async sendTelemetry( + dataService: DataServiceType, + connectionType: ConnectionTypes + ): Promise { + dataService.instance({}, async (error: any, data: any) => { + if (error) { + log.error('TELEMETRY data service error', error); + } + if (data) { + try { + const firstServerHostname = dataService.client.model.hosts[0].host; + const cloudInfo = await this.getCloudInfoFromDataService( + firstServerHostname + ); + const nonGenuineServerName = data.genuineMongoDB.isGenuine + ? null + : data.genuineMongoDB.dbType; + const telemetryData = { + isAtlas: !!data.client.s.url.match(ATLAS_REGEX), + isLocalhost: !!data.client.s.url.match(LOCALHOST_REGEX), + isDataLake: data.dataLake.isDataLake, + isEnterprise: data.build.enterprise_module, + isPublicCloud: cloudInfo.isPublicCloud, + publicCloudName: cloudInfo.publicCloudName, + isGenuine: data.genuineMongoDB.isGenuine, + nonGenuineServerName, + serverVersion: data.build.version, + serverArch: data.build.raw.buildEnvironment.target_arch, + serverOS: data.build.raw.buildEnvironment.target_os, + isUsedConnectScreen: + connectionType === ConnectionTypes.CONNECTION_FORM, + isUsedCommandPalette: + connectionType === ConnectionTypes.CONNECTION_STRING, + isUsedSavedConnection: + connectionType === ConnectionTypes.CONNECTION_ID + }; + + // Send metrics to Segment + this._telemetryController?.track( + TelemetryEventTypes.NEW_CONNECTION, + telemetryData + ); + } catch (error) { + log.error('TELEMETRY cloud info error', error); + } + } + }); + } + public connect = async ( connectionId: string, - connectionModel: ConnectionModelType + connectionModel: ConnectionModelType, + connectionType: ConnectionTypes ): Promise => { log.info( 'Connect called to connect to instance:', @@ -327,6 +419,7 @@ export default class ConnectionController { connectionModel.appname = `${name} ${version}`; const newDataService: DataServiceType = new DataService(connectionModel); + newDataService.connect((err: Error | undefined) => { this._statusView.hideMessage(); @@ -348,6 +441,10 @@ export default class ConnectionController { this.eventEmitter.emit(DataServiceEventTypes.CONNECTIONS_DID_CHANGE); this.eventEmitter.emit(DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED); + if (this._telemetryController) { + this.sendTelemetry(newDataService, connectionType); + } + return resolve(true); }); }); @@ -372,13 +469,14 @@ export default class ConnectionController { return Promise.resolve(false); } return new Promise((resolve) => { - this.connect(connectionId, connectionModel).then( - resolve, - (err: Error) => { - vscode.window.showErrorMessage(err.message); - return resolve(false); - } - ); + this.connect( + connectionId, + connectionModel, + ConnectionTypes.CONNECTION_ID + ).then(resolve, (err: Error) => { + vscode.window.showErrorMessage(err.message); + return resolve(false); + }); }); } @@ -536,13 +634,13 @@ export default class ConnectionController { const connectionNameToRemove: | string | undefined = await vscode.window.showQuickPick( - connectionIds.map( - (id, index) => `${index + 1}: ${this._connections[id].name}` - ), - { - placeHolder: 'Choose a connection to remove...' - } - ); + connectionIds.map( + (id, index) => `${index + 1}: ${this._connections[id].name}` + ), + { + placeHolder: 'Choose a connection to remove...' + } + ); if (!connectionNameToRemove) { return Promise.resolve(false); diff --git a/src/dataServiceType.ts b/src/dataServiceType.ts index bdf875274..1f1c11cda 100644 --- a/src/dataServiceType.ts +++ b/src/dataServiceType.ts @@ -19,5 +19,7 @@ export type DataServiceType = { callback: (error: Error | undefined, documents: object[]) => void ): void; + instance(opts: any, callback: any): any; + client: any; }; diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index b2a6962d2..0544a1a9b 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -55,7 +55,8 @@ export default class MDBExtensionController implements vscode.Disposable { } else { this._connectionController = new ConnectionController( this._statusView, - this._storageController + this._storageController, + this._telemetryController ); } diff --git a/src/telemetry/telemetryController.ts b/src/telemetry/telemetryController.ts index 4d2133b07..2b215b9c1 100644 --- a/src/telemetry/telemetryController.ts +++ b/src/telemetry/telemetryController.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import { config } from 'dotenv'; import { StorageController } from '../storage'; -const log = createLogger('analytics'); +const log = createLogger('telemetry'); const fs = require('fs'); type PlaygroundTelemetryEventProperties = { @@ -21,15 +21,34 @@ type ExtensionCommandRunTelemetryEventProperties = { command: string; }; +type NewConnectionTelemetryEventProperties = { + isAtlas: boolean; + isLocalhost: boolean; + isDataLake: boolean; + isEnterprise: boolean; + isPublicCloud: boolean; + publicCloudName: string | null; + isGenuine: boolean; + nonGenuineServerName: string | null; + serverVersion: string; + serverArch: string; + serverOS: string; + isUsedConnectScreen: boolean; + isUsedCommandPalette: boolean; + isUsedSavedConnection: boolean; +}; + export type TelemetryEventProperties = | PlaygroundTelemetryEventProperties | LinkClickedTelemetryEventProperties - | ExtensionCommandRunTelemetryEventProperties; + | ExtensionCommandRunTelemetryEventProperties + | NewConnectionTelemetryEventProperties; export enum TelemetryEventTypes { PLAYGROUND_CODE_EXECUTED = 'playground code executed', EXTENSION_LINK_CLICKED = 'link clicked', - EXTENSION_COMMAND_RUN = 'command run' + EXTENSION_COMMAND_RUN = 'command run', + NEW_CONNECTION = 'new connection' } /** diff --git a/src/test/suite/connectionController.test.ts b/src/test/suite/connectionController.test.ts index a65cddaed..337b3bfc3 100644 --- a/src/test/suite/connectionController.test.ts +++ b/src/test/suite/connectionController.test.ts @@ -58,8 +58,7 @@ suite('Connection Controller Test Suite', () => { ); const connnectionId = testConnectionController.getActiveConnectionId() || ''; - const name = - testConnectionController._connections[connnectionId].name; + const name = testConnectionController._connections[connnectionId].name; assert( name === 'localhost:27018', `Expected active connection to be 'localhost:27018' found ${name}` @@ -465,7 +464,10 @@ suite('Connection Controller Test Suite', () => { await vscode.workspace .getConfiguration('mdb.connectionSaving') - .update('defaultConnectionSavingLocation', DefaultSavingLocations.Workspace); + .update( + 'defaultConnectionSavingLocation', + DefaultSavingLocations.Workspace + ); await testConnectionController.addNewConnectionStringAndConnect( TEST_DATABASE_URI @@ -496,7 +498,9 @@ suite('Connection Controller Test Suite', () => { assert( connections[Object.keys(connections)[2]].driverUrl === expectedDriverUri, - `Expected loaded connection to include driver url '${expectedDriverUri}' found '${connections[Object.keys(connections)[2]].driverUrl}'` + `Expected loaded connection to include driver url '${expectedDriverUri}' found '${ + connections[Object.keys(connections)[2]].driverUrl + }'` ); }); @@ -669,12 +673,14 @@ suite('Connection Controller Test Suite', () => { // Activate (which will load the past connection). testConnectionController.loadSavedConnections().then(() => { assert( - testConnectionController.getSavedConnections().length === 1, + testConnectionController.getSavedConnections().length === + 1, `Expected 1 connection config, found ${ testConnectionController.getSavedConnections().length }.` ); - const id = testConnectionController.getSavedConnections()[0].id; + const id = testConnectionController.getSavedConnections()[0] + .id; testConnectionController .connectWithConnectionId(id) @@ -753,7 +759,7 @@ suite('Connection Controller Test Suite', () => { ); testConnectionController.loadSavedConnections().then(() => { - // Don't save connections on default. + // Don't save connections on default. vscode.workspace .getConfiguration('mdb.connectionSaving') .update( @@ -761,8 +767,8 @@ suite('Connection Controller Test Suite', () => { DefaultSavingLocations['Session Only'] ) .then(() => { - // Updating a setting sometimes takes a bit on vscode, and it doesnt - // return a usable promise. Timeout to ensure it sets. + // Updating a setting sometimes takes a bit on vscode, and it doesnt + // return a usable promise. Timeout to ensure it sets. setTimeout(() => { testConnectionController .addNewConnectionStringAndConnect(TEST_DATABASE_URI) @@ -811,8 +817,8 @@ suite('Connection Controller Test Suite', () => { DefaultSavingLocations.Workspace ) .then(() => { - // Updating a setting sometimes takes a bit on vscode, and it doesnt - // return a usable promise. Timeout to ensure it sets. + // Updating a setting sometimes takes a bit on vscode, and it doesnt + // return a usable promise. Timeout to ensure it sets. setTimeout(() => { testConnectionController .addNewConnectionStringAndConnect(TEST_DATABASE_URI) @@ -830,9 +836,11 @@ suite('Connection Controller Test Suite', () => { ); const connectionId = - testConnectionController.getActiveConnectionId() || 'a'; + testConnectionController.getActiveConnectionId() || 'a'; testConnectionController.disconnect(); - await testConnectionController.removeSavedConnection(connectionId); + await testConnectionController.removeSavedConnection( + connectionId + ); const postWorkspaceStoreConnections = testStorageController.get( StorageVariables.WORKSPACE_SAVED_CONNECTIONS, @@ -944,23 +952,24 @@ suite('Connection Controller Test Suite', () => { .then((renameSuccess) => { assert(renameSuccess); - testConnectionController - .disconnect() - .then(() => { - testConnectionController.clearAllConnections(); - assert( - testConnectionController.getSavedConnections() - .length === 0, - 'Expected no saved connection.' - ); - - // Activate (which will load the past connection). - testConnectionController.loadSavedConnections().then(() => { + testConnectionController.disconnect().then(() => { + testConnectionController.clearAllConnections(); + assert( + testConnectionController.getSavedConnections() + .length === 0, + 'Expected no saved connection.' + ); + + // Activate (which will load the past connection). + testConnectionController + .loadSavedConnections() + .then(() => { assert( testConnectionController.getSavedConnections() .length === 1, `Expected 1 connection config, found ${ - testConnectionController.getSavedConnections().length + testConnectionController.getSavedConnections() + .length }.` ); const id = testConnectionController.getSavedConnections()[0] @@ -973,8 +982,9 @@ suite('Connection Controller Test Suite', () => { name === 'new connection name', `Expected the active connection name to be 'new connection name', found '${name}'.` ); - }).then(done, done); - }, done); + }) + .then(done, done); + }, done); }, done); }, done); }, 50); diff --git a/src/test/suite/editors/activeDBCodeLensProvider.test.ts b/src/test/suite/editors/activeDBCodeLensProvider.test.ts index a9dbfac8f..08b6275df 100644 --- a/src/test/suite/editors/activeDBCodeLensProvider.test.ts +++ b/src/test/suite/editors/activeDBCodeLensProvider.test.ts @@ -16,7 +16,9 @@ suite('Active DB CodeLens Provider Test Suite', () => { new StatusView(mockExtensionContext), mockStorageController ); - const testCodeLensProvider = new ActiveDBCodeLensProvider(testConnectionController); + const testCodeLensProvider = new ActiveDBCodeLensProvider( + testConnectionController + ); const codeLens = testCodeLensProvider.provideCodeLenses(); assert(!!codeLens); @@ -28,7 +30,9 @@ suite('Active DB CodeLens Provider Test Suite', () => { new StatusView(mockExtensionContext), mockStorageController ); - const testCodeLensProvider = new ActiveDBCodeLensProvider(testConnectionController); + const testCodeLensProvider = new ActiveDBCodeLensProvider( + testConnectionController + ); const mockActiveConnection = { find: (namespace, filter, options, callback): void => { return callback(null, ['Text message']); @@ -43,8 +47,14 @@ suite('Active DB CodeLens Provider Test Suite', () => { assert(codeLens.length === 1); const range = codeLens[0].range; const expectedStartLine = 0; - assert(range.start.line === expectedStartLine, `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}`); + assert( + range.start.line === expectedStartLine, + `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}` + ); const expectedEnd = 0; - assert(range.end.line === expectedEnd, `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}`); + assert( + range.end.line === expectedEnd, + `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}` + ); }); }); diff --git a/src/test/suite/editors/collectionDocumentsProvider.test.ts b/src/test/suite/editors/collectionDocumentsProvider.test.ts index cf9c4b50c..caedfa5e2 100644 --- a/src/test/suite/editors/collectionDocumentsProvider.test.ts +++ b/src/test/suite/editors/collectionDocumentsProvider.test.ts @@ -64,23 +64,29 @@ suite('Collection Documents Provider Test Suite', () => { `scheme:Results: filename.json?namespace=my-favorite-fruit-is.pineapple&operationId=${operationId}` ); - testCollectionViewProvider.provideTextDocumentContent(uri).then(documents => { - assert( - documents.includes('Declaration of Independence'), - `Expected provideTextDocumentContent to return documents string, found ${documents}` - ); - done(); - }).catch(done); + testCollectionViewProvider + .provideTextDocumentContent(uri) + .then((documents) => { + assert( + documents.includes('Declaration of Independence'), + `Expected provideTextDocumentContent to return documents string, found ${documents}` + ); + done(); + }) + .catch(done); }); test('expected provideTextDocumentContent to return a ejson.stringify string', (done) => { - const mockDocuments = [{ - _id: 'first_id', - field1: 'first_field' - }, { - _id: 'first_id', - field1: 'first_field' - }]; + const mockDocuments = [ + { + _id: 'first_id', + field1: 'first_field' + }, + { + _id: 'first_id', + field1: 'first_field' + } + ]; const mockActiveConnection = { find: (namespace, filter, options, callback): void => { @@ -109,13 +115,16 @@ suite('Collection Documents Provider Test Suite', () => { `scheme:Results: filename.json?namespace=test.test&operationId=${operationId}` ); - testCollectionViewProvider.provideTextDocumentContent(uri).then(documents => { - assert( - documents === mockDocumentsAsJsonString, - `Expected provideTextDocumentContent to return ejson stringified string, found ${documents}` - ); - done(); - }).catch(done); + testCollectionViewProvider + .provideTextDocumentContent(uri) + .then((documents) => { + assert( + documents === mockDocumentsAsJsonString, + `Expected provideTextDocumentContent to return ejson stringified string, found ${documents}` + ); + done(); + }) + .catch(done); }); test('expected provideTextDocumentContent to set hasMoreDocumentsToShow to false when there arent more documents', (done) => { @@ -138,7 +147,6 @@ suite('Collection Documents Provider Test Suite', () => { mockConnectionController, testQueryStore, new StatusView(mockExtensionContext) - ); const operationId = testQueryStore.createNewOperation(); @@ -151,17 +159,23 @@ suite('Collection Documents Provider Test Suite', () => { ); testCollectionViewProvider.provideTextDocumentContent(uri).then(() => { - assert(testQueryStore.operations[operationId].hasMoreDocumentsToShow === false, 'Expected not to have more documents to show.'); + assert( + testQueryStore.operations[operationId].hasMoreDocumentsToShow === false, + 'Expected not to have more documents to show.' + ); // Reset and test inverse. testQueryStore.operations[operationId].currentLimit = 2; testQueryStore.operations[operationId].hasMoreDocumentsToShow = true; - testCollectionViewProvider.provideTextDocumentContent(uri).then(() => { - assert(testQueryStore.operations[operationId].hasMoreDocumentsToShow); + testCollectionViewProvider + .provideTextDocumentContent(uri) + .then(() => { + assert(testQueryStore.operations[operationId].hasMoreDocumentsToShow); - done(); - }).catch(done); + done(); + }) + .catch(done); }); }); @@ -197,7 +211,12 @@ suite('Collection Documents Provider Test Suite', () => { const mockHideMessage = sinon.fake(); sinon.replace(textStatusView, 'hideMessage', mockHideMessage); - mockActiveConnection.find = (namespace, filter, options, callback): void => { + mockActiveConnection.find = ( + namespace, + filter, + options, + callback + ): void => { assert(mockShowMessage.called); assert(!mockHideMessage.called); assert(mockShowMessage.firstArg === 'Fetching documents...'); @@ -205,8 +224,11 @@ suite('Collection Documents Provider Test Suite', () => { return callback(null, ['aaaaaaaaaaaaaaaaa']); }; - testCollectionViewProvider.provideTextDocumentContent(uri).then(() => { - assert(mockHideMessage.called); - }).then(done, done); + testCollectionViewProvider + .provideTextDocumentContent(uri) + .then(() => { + assert(mockHideMessage.called); + }) + .then(done, done); }); }); diff --git a/src/test/suite/explorer/explorerController.test.ts b/src/test/suite/explorer/explorerController.test.ts index b4101a4d7..19bd0d20b 100644 --- a/src/test/suite/explorer/explorerController.test.ts +++ b/src/test/suite/explorer/explorerController.test.ts @@ -17,6 +17,11 @@ const testDatabaseURI2WithTimeout = suite('Explorer Controller Test Suite', () => { beforeEach(async () => { + sinon.replace( + mdbTestExtension.testExtensionController._connectionController, + '_telemetryController', + {} + ); // Don't save connections on default. await vscode.workspace .getConfiguration('mdb.connectionSaving') @@ -121,8 +126,7 @@ suite('Explorer Controller Test Suite', () => { ); const activeId = testConnectionController.getActiveConnectionId(); assert( - activeId === - Object.keys(testConnectionController._connections)[0], + activeId === Object.keys(testConnectionController._connections)[0], `Expected active connection to be '${ Object.keys(testConnectionController._connections)[0] }' found ${activeId}` diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts index 067bd46b9..30f43eeb7 100644 --- a/src/test/suite/extension.test.ts +++ b/src/test/suite/extension.test.ts @@ -96,11 +96,20 @@ suite('Extension Test Suite', () => { const testConnectionController = mockMDBExtension._connectionController; const fakeVscodeInfoMessage = sinon.fake(); + sinon.replace( vscode.window, 'showInformationMessage', fakeVscodeInfoMessage ); + sinon.replace( + testConnectionController._telemetryController, + 'track', + () => {} + ); + sinon.replace(testConnectionController, 'getCloudInfoFromDataService', () => + Promise.resolve() + ); testConnectionController .addNewConnectionStringAndConnect(TEST_DATABASE_URI) diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index b10244c2e..d0f7aee55 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -16,20 +16,21 @@ import { mdbTestExtension } from './stubbableMdbExtension'; import ConnectionController from '../../connectionController'; import { StorageController } from '../../storage'; import { StorageScope } from '../../storage/storageController'; +import TelemetryController from '../../telemetry/telemetryController'; const sinon = require('sinon'); const testDatabaseURI = 'mongodb://localhost:27018'; suite('MDBExtensionController Test Suite', () => { beforeEach(() => { - // Here we stub the showInformationMessage process because it is too much - // for the render process and leads to crashes while testing. - sinon.replace(vscode.window, 'showInformationMessage', sinon.stub()); sinon.replace( mdbTestExtension.testExtensionController._telemetryController, 'track', () => {} ); + // Here we stub the showInformationMessage process because it is too much + // for the render process and leads to crashes while testing. + sinon.replace(vscode.window, 'showInformationMessage', sinon.stub()); }); afterEach(() => { sinon.restore(); @@ -780,11 +781,20 @@ suite('MDBExtensionController Test Suite', () => { }); test('mdb.dropCollection fails when a collection doesnt exist', (done) => { + const mockExtensionContext = mdbTestExtension.testExtensionContext; + const mockStorageController = new StorageController(mockExtensionContext); + const mockTelemetryController = new TelemetryController( + mockStorageController, + mockExtensionContext + ); const testConnectionController = new ConnectionController( mdbTestExtension.testExtensionController._statusView, - new StorageController(mdbTestExtension.testExtensionContext) + mockStorageController, + mockTelemetryController ); + mockTelemetryController.track = () => {}; + testConnectionController .addNewConnectionStringAndConnect(testDatabaseURI) .then(() => { @@ -897,11 +907,20 @@ suite('MDBExtensionController Test Suite', () => { }); test('mdb.dropDatabase succeeds even when a database doesnt exist (mdb behavior)', (done) => { + const mockExtensionContext = mdbTestExtension.testExtensionContext; + const mockStorageController = new StorageController(mockExtensionContext); + const mockTelemetryController = new TelemetryController( + mockStorageController, + mockExtensionContext + ); const testConnectionController = new ConnectionController( mdbTestExtension.testExtensionController._statusView, - new StorageController(mdbTestExtension.testExtensionContext) + mockStorageController, + mockTelemetryController ); + mockTelemetryController.track = () => {}; + testConnectionController .addNewConnectionStringAndConnect(testDatabaseURI) .then(() => { @@ -1015,8 +1034,7 @@ suite('MDBExtensionController Test Suite', () => { ); assert( mdbTestExtension.testExtensionController._connectionController - ._connections.blueBerryPancakesAndTheSmellOfBacon.name === - 'NAAAME', + ._connections.blueBerryPancakesAndTheSmellOfBacon.name === 'NAAAME', 'Expected connection not to be ranamed.' ); mdbTestExtension.testExtensionController._connectionController.clearAllConnections(); diff --git a/src/test/suite/telemetry/telemetryController.test.ts b/src/test/suite/telemetry/telemetryController.test.ts index 5d2ff3398..4b125f716 100644 --- a/src/test/suite/telemetry/telemetryController.test.ts +++ b/src/test/suite/telemetry/telemetryController.test.ts @@ -3,14 +3,19 @@ import { StorageController } from '../../../storage'; import { TestExtensionContext } from '../stubs'; import { resolve } from 'path'; import { config } from 'dotenv'; -import { TelemetryController } from '../../../telemetry'; +import TelemetryController, { + TelemetryEventTypes +} from '../../../telemetry/telemetryController'; import { mdbTestExtension } from '../stubbableMdbExtension'; import { afterEach, beforeEach } from 'mocha'; const sinon = require('sinon'); const chai = require('chai'); +const sinonChai = require('sinon-chai'); const expect = chai.expect; +chai.use(sinonChai); + config({ path: resolve(__dirname, '../../../../.env') }); suite('Telemetry Controller Test Suite', () => { @@ -20,6 +25,7 @@ suite('Telemetry Controller Test Suite', () => { const mockStorageController = new StorageController(mockExtensionContext); let mockTelemetryTrackMethod: void; let mockExecuteAllMethod: Promise; + let mockGetCloudInfoFromDataService: Promise; beforeEach(() => { mockTelemetryTrackMethod = sinon.fake.resolves(); @@ -36,7 +42,17 @@ suite('Telemetry Controller Test Suite', () => { 'executeAll', mockExecuteAllMethod ); + mockGetCloudInfoFromDataService = sinon.fake.resolves({ + isPublicCloud: false, + publicCloudName: null + }); + sinon.replace( + mdbTestExtension.testExtensionController._connectionController, + 'getCloudInfoFromDataService', + mockGetCloudInfoFromDataService + ); }); + afterEach(() => { sinon.restore(); }); @@ -72,9 +88,13 @@ suite('Telemetry Controller Test Suite', () => { vscode.commands .executeCommand('mdb.showActiveConnectionInPlayground', 'Test') .then(() => { - sinon.assert.calledWith(mockTelemetryTrackMethod, 'command run', { - command: 'mdb.showActiveConnectionInPlayground' - }); + sinon.assert.calledWith( + mockTelemetryTrackMethod, + TelemetryEventTypes.EXTENSION_COMMAND_RUN, + { + command: 'mdb.showActiveConnectionInPlayground' + } + ); }) .then(done, done); }); @@ -86,7 +106,7 @@ suite('Telemetry Controller Test Suite', () => { sinon.assert.calledWith( mockTelemetryTrackMethod, - 'playground code executed', + TelemetryEventTypes.PLAYGROUND_CODE_EXECUTED, { type: 'other' } diff --git a/src/views/webview-app/components/connect-form/connection-form.tsx b/src/views/webview-app/components/connect-form/connection-form.tsx index 3c3c70ba4..ac07a9e5e 100644 --- a/src/views/webview-app/components/connect-form/connection-form.tsx +++ b/src/views/webview-app/components/connect-form/connection-form.tsx @@ -136,16 +136,15 @@ class ConnectionForm extends React.Component { * @returns {React.Component} */ renderURIConnectionMessage(): React.ReactNode { - const { - isUriConnected, - uriConnectionMessage - } = this.props; + const { isUriConnected, uriConnectionMessage } = this.props; if (isUriConnected) { return (
-
{`${uriConnectionMessage} You may now close this window.`}
+
{`${uriConnectionMessage} You may now close this window.`}
);