From 7116208d63cb2049007a4caa959350138fb62bb7 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Wed, 13 Jan 2021 23:44:02 +0100 Subject: [PATCH 1/6] feat: add telemetry events for documents edited from a playground (VSCODE-224) --- package-lock.json | 6 + package.json | 1 + src/connectionController.ts | 11 +- src/editors/editDocumentCodeLensProvider.ts | 54 +++- src/editors/editorsController.ts | 18 +- src/editors/mongoDBDocumentService.ts | 18 +- src/editors/playgroundController.ts | 10 +- src/editors/playgroundResultProvider.ts | 2 +- src/explorer/helpExplorer.ts | 6 +- src/explorer/helpTree.ts | 10 +- src/mdbExtensionController.ts | 31 ++- src/telemetry/index.ts | 4 +- ...metryController.ts => telemetryService.ts} | 123 +++++---- src/test/suite/connectionController.test.ts | 6 +- .../editors/activeDBCodeLensProvider.test.ts | 8 +- .../collectionDocumentsProvider.test.ts | 18 +- .../editDocumentCodeLensProvider.test.ts | 242 +++++++++--------- .../editors/mongoDBDocumentService.test.ts | 28 +- .../editors/playgroundController.test.ts | 10 +- .../editors/playgroundResultProvider.test.ts | 10 +- src/test/suite/explorer/helpExplorer.test.ts | 20 +- .../language/languageServerController.test.ts | 8 +- src/test/suite/mdbExtensionController.test.ts | 149 ++++++++++- .../telemetry/telemetryController.test.ts | 70 +++-- .../suite/views/webviewController.test.ts | 70 ++--- src/utils/types.ts | 7 + src/views/webviewController.ts | 10 +- 27 files changed, 607 insertions(+), 343 deletions(-) rename src/telemetry/{telemetryController.ts => telemetryService.ts} (73%) diff --git a/package-lock.json b/package-lock.json index 283a4caef..e23b36b23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1742,6 +1742,12 @@ "@testing-library/dom": "^5.6.1" } }, + "@types/analytics-node": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/analytics-node/-/analytics-node-3.1.4.tgz", + "integrity": "sha512-i6cqjFotMq1dEwXxyXRqnzp/HmWPCskptrVUQ1UzRIGs/zICFWM2bIJyLt6f9A9/+qE98wls1AHWPQ4WXYS0HA==", + "dev": true + }, "@types/babel__core": { "version": "7.1.12", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", diff --git a/package.json b/package.json index 2aa8cbea6..2e74e3d6c 100644 --- a/package.json +++ b/package.json @@ -867,6 +867,7 @@ "ws": "^7.2.3" }, "devDependencies": { + "@types/analytics-node": "^3.1.4", "@types/chai": "^4.2.9", "@types/chai-fs": "^2.0.2", "@types/chai-json-schema": "^1.4.5", diff --git a/src/connectionController.ts b/src/connectionController.ts index a77bb50e6..be4e9c475 100644 --- a/src/connectionController.ts +++ b/src/connectionController.ts @@ -10,7 +10,7 @@ import { StatusView } from './views'; import { EventEmitter } from 'events'; import { StorageController, StorageVariables } from './storage'; import { SavedConnection, StorageScope } from './storage/storageController'; -import TelemetryController from './telemetry/telemetryController'; +import TelemetryService from './telemetry/telemetryService'; import { ext } from './extensionConstants'; import { CONNECTION_STATUS } from './views/webview-app/extension-app-message-constants'; import SSH_TUNNEL_TYPES from './views/webview-app/connection-model/constants/ssh-tunnel-types'; @@ -72,7 +72,7 @@ export default class ConnectionController { private _statusView: StatusView; private _storageController: StorageController; - private _telemetryController: TelemetryController; + private _telemetryService: TelemetryService; // Used by other parts of the extension that respond to changes in the connections. private eventEmitter: EventEmitter = new EventEmitter(); @@ -80,11 +80,11 @@ export default class ConnectionController { constructor( _statusView: StatusView, storageController: StorageController, - telemetryController: TelemetryController + telemetryService: TelemetryService ) { this._statusView = _statusView; this._storageController = storageController; - this._telemetryController = telemetryController; + this._telemetryService = telemetryService; } _loadSavedConnection = async ( @@ -250,8 +250,7 @@ export default class ConnectionController { newDataService: DataServiceType, connectionType: ConnectionTypes ): void { - // Send metrics to Segment - this._telemetryController.trackNewConnection( + this._telemetryService.trackNewConnection( newDataService, connectionType ); diff --git a/src/editors/editDocumentCodeLensProvider.ts b/src/editors/editDocumentCodeLensProvider.ts index d5441a7f2..790cd1e4c 100644 --- a/src/editors/editDocumentCodeLensProvider.ts +++ b/src/editors/editDocumentCodeLensProvider.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import EXTENSION_COMMANDS from '../commands'; import type { OutputItem, ResultCodeLensInfo } from '../utils/types'; import ConnectionController from '../connectionController'; +import { DOCUMENT_SOURCE_PLAYGROUND } from '../telemetry/telemetryService'; export default class EditDocumentCodeLensProvider implements vscode.CodeLensProvider { @@ -22,7 +23,9 @@ implements vscode.CodeLensProvider { }); } - updateCodeLensesPosition(playgroundResult: OutputItem): void { + updateCodeLensesForPlayground(playgroundResult: OutputItem) { + const source = DOCUMENT_SOURCE_PLAYGROUND; + if (!playgroundResult || !playgroundResult.content) { this._codeLensesInfo = []; @@ -30,24 +33,39 @@ implements vscode.CodeLensProvider { } const { content, namespace, type } = playgroundResult; - const connectionId = this._connectionController.getActiveConnectionId(); - const codeLensesInfo: ResultCodeLensInfo[] = []; // Show code lenses only for the list of documents or a single document // that are returned by the find() method. - if (type === 'Cursor' && Array.isArray(content)) { + if (type === 'Cursor') { + this._updateCodeLensesForCursor({ content, namespace, source }); + } else if (type === 'Document') { + this._updateCodeLensesForDocument({ content, namespace, source }); + } + } + + _updateCodeLensesForCursor(data: { + content: any, + namespace: string | null, + source: string + }) { + const codeLensesInfo: ResultCodeLensInfo[] = []; + + if (Array.isArray(data.content)) { + const connectionId = this._connectionController.getActiveConnectionId(); + // When the playground result is the collection, // show the first code lense after [{. let line = 2; - content.forEach((item) => { + data.content.forEach((item) => { // We need _id and namespace for code lenses // to be able to save the editable document. - if (item !== null && item._id && namespace) { + if (item !== null && item._id && data.namespace) { codeLensesInfo.push({ + source: data.source, line, documentId: item._id, - namespace, + namespace: data.namespace, connectionId }); // To calculate the position of the next open curly bracket, @@ -56,13 +74,29 @@ implements vscode.CodeLensProvider { line += JSON.stringify(item, null, 2).split(/\r\n|\r|\n/).length; } }); - } else if (type === 'Document' && content._id && namespace) { + } + + this._codeLensesInfo = codeLensesInfo; + this._onDidChangeCodeLenses.fire(); + } + + _updateCodeLensesForDocument(data: { + content: any, + namespace: string | null, + source: string + }): void { + const codeLensesInfo: ResultCodeLensInfo[] = []; + + if (data.content._id && data.namespace) { + const connectionId = this._connectionController.getActiveConnectionId(); + // When the playground result is the single document, // show the single code lense after {. codeLensesInfo.push({ + source: data.source, line: 1, - documentId: content._id, - namespace, + documentId: data.content._id, + namespace: data.namespace, connectionId }); } diff --git a/src/editors/editorsController.ts b/src/editors/editorsController.ts index 7f680d16e..d9b4105c9 100644 --- a/src/editors/editorsController.ts +++ b/src/editors/editorsController.ts @@ -17,10 +17,11 @@ import PlaygroundController from './playgroundController'; import DocumentIdStore from './documentIdStore'; import MongoDBDocumentService, { DOCUMENT_ID_URI_IDENTIFIER, + DOCUMENT_SOURCE_URI_IDENTIFIER, VIEW_DOCUMENT_SCHEME } from './mongoDBDocumentService'; import { MemoryFileSystemProvider } from './memoryFileSystemProvider'; -import TelemetryController from '../telemetry/telemetryController'; +import TelemetryService from '../telemetry/telemetryService'; import PlaygroundResultProvider, { PLAYGROUND_RESULT_SCHEME } from './playgroundResultProvider'; @@ -42,7 +43,7 @@ export default class EditorsController { _memoryFileSystemProvider: MemoryFileSystemProvider; _documentIdStore: DocumentIdStore; _mongoDBDocumentService: MongoDBDocumentService; - _telemetryController: TelemetryController; + _telemetryService: TelemetryService; _playgroundResultViewProvider: PlaygroundResultProvider; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; _partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider; @@ -52,7 +53,7 @@ export default class EditorsController { connectionController: ConnectionController, playgroundController: PlaygroundController, statusView: StatusView, - telemetryController: TelemetryController, + telemetryService: TelemetryService, playgroundResultViewProvider: PlaygroundResultProvider, activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider, partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider @@ -63,7 +64,7 @@ export default class EditorsController { this._playgroundController = playgroundController; this._context = context; this._statusView = statusView; - this._telemetryController = telemetryController; + this._telemetryService = telemetryService; this._memoryFileSystemProvider = new MemoryFileSystemProvider(); this._documentIdStore = new DocumentIdStore(); this._mongoDBDocumentService = new MongoDBDocumentService( @@ -71,7 +72,7 @@ export default class EditorsController { this._documentIdStore, this._connectionController, this._statusView, - this._telemetryController + this._telemetryService ); this._collectionViewProvider = new CollectionDocumentsProvider( connectionController, @@ -123,8 +124,9 @@ export default class EditorsController { const connectionIdUriQuery = `${CONNECTION_ID_URI_IDENTIFIER}=${activeConnectionId}`; const documentIdReference = this._documentIdStore.add(data.documentId); const documentIdUriQuery = `${DOCUMENT_ID_URI_IDENTIFIER}=${documentIdReference}`; + const documentSourceUriQuery = `${DOCUMENT_SOURCE_URI_IDENTIFIER}=${data.source}`; const uri: vscode.Uri = vscode.Uri.parse(fileName).with({ - query: `?${namespaceUriQuery}&${connectionIdUriQuery}&${documentIdUriQuery}` + query: `?${namespaceUriQuery}&${connectionIdUriQuery}&${documentIdUriQuery}&${documentSourceUriQuery}` }); const document = await vscode.workspace.openTextDocument(uri); @@ -154,6 +156,7 @@ export default class EditorsController { const connectionId = uriParams.get(CONNECTION_ID_URI_IDENTIFIER); const documentIdReference = uriParams.get(DOCUMENT_ID_URI_IDENTIFIER) || ''; const documentId = this._documentIdStore.get(documentIdReference); + const source = uriParams.get(DOCUMENT_SOURCE_URI_IDENTIFIER) || ''; // If not MongoDB document save to disk instead of MongoDB. if ( @@ -176,7 +179,8 @@ export default class EditorsController { namespace, connectionId, documentId, - newDocument + newDocument, + source }); // Save document changes to active editor. diff --git a/src/editors/mongoDBDocumentService.ts b/src/editors/mongoDBDocumentService.ts index ea2b8afc8..a406fcb25 100644 --- a/src/editors/mongoDBDocumentService.ts +++ b/src/editors/mongoDBDocumentService.ts @@ -3,13 +3,15 @@ import { EJSON } from 'bson'; import DocumentIdStore from './documentIdStore'; import ConnectionController from '../connectionController'; import { StatusView } from '../views'; -import TelemetryController from '../telemetry/telemetryController'; +import TelemetryService, { DOCUMENT_SOURCE_TREEVIEW } from '../telemetry/telemetryService'; import { createLogger } from '../logging'; import util from 'util'; import type { ResultCodeLensInfo } from '../utils/types'; export const DOCUMENT_ID_URI_IDENTIFIER = 'documentId'; +export const DOCUMENT_SOURCE_URI_IDENTIFIER = 'source'; + export const VIEW_DOCUMENT_SCHEME = 'VIEW_DOCUMENT_SCHEME'; const log = createLogger('document controller'); @@ -19,20 +21,20 @@ export default class MongoDBDocumentService { _documentIdStore: DocumentIdStore; _connectionController: ConnectionController; _statusView: StatusView; - _telemetryController: TelemetryController; + _telemetryService: TelemetryService; constructor( context: vscode.ExtensionContext, documentIdStore: DocumentIdStore, connectionController: ConnectionController, statusView: StatusView, - telemetryController: TelemetryController + telemetryService: TelemetryService ) { this._context = context; this._documentIdStore = documentIdStore; this._connectionController = connectionController; this._statusView = statusView; - this._telemetryController = telemetryController; + this._telemetryService = telemetryService; } _fetchDocumentFailed(message: string): void { @@ -44,8 +46,7 @@ export default class MongoDBDocumentService { _saveDocumentFailed(message: string): void { const errorMessage = `Unable to save document: ${message}`; - // Send a telemetry event that saving the document failed. - this._telemetryController.trackDocumentUpdated('treeview', false); + this._telemetryService.trackDocumentUpdated(DOCUMENT_SOURCE_TREEVIEW, false); throw new Error(errorMessage); } @@ -55,10 +56,11 @@ export default class MongoDBDocumentService { namespace: string; connectionId: string; newDocument: EJSON.SerializableTypes; + source: string; }): Promise { log.info('replace document in MongoDB', data); - const { documentId, namespace, connectionId, newDocument } = data; + const { documentId, namespace, connectionId, newDocument, source } = data; const activeConnectionId = this._connectionController.getActiveConnectionId(); const connectionName = this._connectionController.getSavedConnectionName( connectionId @@ -97,7 +99,7 @@ export default class MongoDBDocumentService { ); this._statusView.hideMessage(); - this._telemetryController.trackDocumentUpdated('treeview', true); + this._telemetryService.trackDocumentUpdated(source, true); } catch (error) { this._statusView.hideMessage(); diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 5bc488bae..fdbc8cd86 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -3,7 +3,7 @@ import ConnectionController, { DataServiceEventTypes } from '../connectionController'; import { LanguageServerController } from '../language'; -import TelemetryController from '../telemetry/telemetryController'; +import TelemetryService from '../telemetry/telemetryService'; import ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider'; import PartialExecutionCodeLensProvider from './partialExecutionCodeLensProvider'; import { OutputChannel, ProgressLocation, TextEditor } from 'vscode'; @@ -31,7 +31,7 @@ export default class PlaygroundController { playgroundResult?: OutputItem; _context: vscode.ExtensionContext; _languageServerController: LanguageServerController; - _telemetryController: TelemetryController; + _telemetryService: TelemetryService; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; _partialExecutionCodeLensProvider: PartialExecutionCodeLensProvider; _outputChannel: OutputChannel; @@ -49,7 +49,7 @@ export default class PlaygroundController { context: vscode.ExtensionContext, connectionController: ConnectionController, languageServerController: LanguageServerController, - telemetryController: TelemetryController, + telemetryService: TelemetryService, statusView: StatusView, playgroundResultViewProvider: PlaygroundResultProvider, activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider, @@ -60,7 +60,7 @@ export default class PlaygroundController { this._isPartialRun = false; this.connectionController = connectionController; this._languageServerController = languageServerController; - this._telemetryController = telemetryController; + this._telemetryService = telemetryService; this._statusView = statusView; this._playgroundResultViewProvider = playgroundResultViewProvider; this._outputChannel = vscode.window.createOutputChannel( @@ -251,7 +251,7 @@ export default class PlaygroundController { ); this._statusView.hideMessage(); - this._telemetryController.trackPlaygroundCodeExecuted( + this._telemetryService.trackPlaygroundCodeExecuted( result, this._isPartialRun, result ? false : true diff --git a/src/editors/playgroundResultProvider.ts b/src/editors/playgroundResultProvider.ts index c657093a7..97e0e16b4 100644 --- a/src/editors/playgroundResultProvider.ts +++ b/src/editors/playgroundResultProvider.ts @@ -65,7 +65,7 @@ implements vscode.TextDocumentContentProvider { return this._playgroundResult.content as string; } - this._editDocumentCodeLensProvider?.updateCodeLensesPosition( + this._editDocumentCodeLensProvider?.updateCodeLensesForPlayground( this._playgroundResult ); diff --git a/src/explorer/helpExplorer.ts b/src/explorer/helpExplorer.ts index 6e25d6a22..05a5920b5 100644 --- a/src/explorer/helpExplorer.ts +++ b/src/explorer/helpExplorer.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import HelpTree from './helpTree'; import { createLogger } from '../logging'; -import { TelemetryController } from '../telemetry'; +import { TelemetryService } from '../telemetry'; const log = createLogger('help and info explorer controller'); @@ -14,14 +14,14 @@ export default class HelpExplorer { this._treeController = new HelpTree(); } - activateHelpTreeView(telemetryController: TelemetryController): void { + activateHelpTreeView(telemetryService: TelemetryService): void { if (!this._treeView) { this._treeView = vscode.window.createTreeView('mongoDBHelpExplorer', { treeDataProvider: this._treeController }); this._treeController.activateTreeViewEventHandlers( this._treeView, - telemetryController + telemetryService ); } } diff --git a/src/explorer/helpTree.ts b/src/explorer/helpTree.ts index 858ab368e..f0f4daa72 100644 --- a/src/explorer/helpTree.ts +++ b/src/explorer/helpTree.ts @@ -3,7 +3,7 @@ import { openLink } from '../utils/linkHelper'; const path = require('path'); import { getImagesPath } from '../extensionConstants'; -import { TelemetryController } from '../telemetry'; +import { TelemetryService } from '../telemetry'; const HELP_LINK_CONTEXT_VALUE = 'HELP_LINK'; @@ -51,13 +51,13 @@ implements vscode.TreeDataProvider { activateTreeViewEventHandlers = ( treeView: vscode.TreeView, - telemetryController: TelemetryController + telemetryService: TelemetryService ): void => { treeView.onDidChangeSelection(async (event: any) => { if (event.selection && event.selection.length === 1) { const selectedItem = event.selection[0]; - await this.onClickHelpItem(selectedItem, telemetryController); + await this.onClickHelpItem(selectedItem, telemetryService); } }); }; @@ -121,9 +121,9 @@ implements vscode.TreeDataProvider { return element.getChildren(); } - async onClickHelpItem(helpItem: HelpLinkTreeItem, telemetryController: TelemetryController): Promise { + async onClickHelpItem(helpItem: HelpLinkTreeItem, telemetryService: TelemetryService): Promise { if (helpItem.contextValue === HELP_LINK_CONTEXT_VALUE) { - telemetryController.trackLinkClicked( + telemetryService.trackLinkClicked( 'helpPanel', helpItem.linkId ); diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index f3ef757bc..25dceb0e7 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -15,7 +15,7 @@ import { } from './explorer'; import EXTENSION_COMMANDS from './commands'; import { LanguageServerController } from './language'; -import TelemetryController from './telemetry/telemetryController'; +import TelemetryService, { DOCUMENT_SOURCE_PLAYGROUND, DOCUMENT_SOURCE_TREEVIEW } from './telemetry/telemetryService'; import { StatusView } from './views'; import { createLogger } from './logging'; import { StorageController, StorageVariables } from './storage'; @@ -47,7 +47,7 @@ export default class MDBExtensionController implements vscode.Disposable { _playgroundsExplorer: PlaygroundsExplorer; _statusView: StatusView; _storageController: StorageController; - _telemetryController: TelemetryController; + _telemetryService: TelemetryService; _languageServerController: LanguageServerController; _webviewController: WebviewController; _playgroundResultViewProvider: PlaygroundResultProvider; @@ -61,7 +61,7 @@ export default class MDBExtensionController implements vscode.Disposable { this._context = context; this._statusView = new StatusView(context); this._storageController = new StorageController(context); - this._telemetryController = new TelemetryController( + this._telemetryService = new TelemetryService( this._storageController, context, options.shouldTrackTelemetry @@ -69,7 +69,7 @@ export default class MDBExtensionController implements vscode.Disposable { this._connectionController = new ConnectionController( this._statusView, this._storageController, - this._telemetryController + this._telemetryService ); this._languageServerController = new LanguageServerController(context); this._explorerController = new ExplorerController( @@ -89,7 +89,7 @@ export default class MDBExtensionController implements vscode.Disposable { context, this._connectionController, this._languageServerController, - this._telemetryController, + this._telemetryService, this._statusView, this._playgroundResultViewProvider, this._activeConnectionCodeLensProvider, @@ -100,24 +100,24 @@ export default class MDBExtensionController implements vscode.Disposable { this._connectionController, this._playgroundController, this._statusView, - this._telemetryController, + this._telemetryService, this._playgroundResultViewProvider, this._activeConnectionCodeLensProvider, this._partialExecutionCodeLensProvider ); this._webviewController = new WebviewController( this._connectionController, - this._telemetryController + this._telemetryService ); this._editorsController.registerProviders(); } activate(): void { this._explorerController.activateConnectionsTreeView(); - this._helpExplorer.activateHelpTreeView(this._telemetryController); + this._helpExplorer.activateHelpTreeView(this._telemetryService); this._playgroundsExplorer.activatePlaygroundsTreeView(); this._connectionController.loadSavedConnections(); - this._telemetryController.activateSegmentAnalytics(); + this._telemetryService.activateSegmentAnalytics(); this._languageServerController.startLanguageServer(); this.registerCommands(); @@ -179,8 +179,11 @@ export default class MDBExtensionController implements vscode.Disposable { ); this.registerCommand( EXTENSION_COMMANDS.MDB_OPEN_MONGODB_DOCUMENT_FROM_PLAYGROUND, - (data: ResultCodeLensInfo) => - this._editorsController.openMongoDBDocument(data) + (data: ResultCodeLensInfo) => { + this._telemetryService.trackOpenMongoDBDocumentFromPlayground(DOCUMENT_SOURCE_PLAYGROUND); + + return this._editorsController.openMongoDBDocument(data); + } ); this.registerCommand(EXTENSION_COMMANDS.MDB_SAVE_MONGODB_DOCUMENT, () => this._editorsController.saveMongoDBDocument() @@ -202,8 +205,7 @@ export default class MDBExtensionController implements vscode.Disposable { commandHandler: (...args: any[]) => Promise ): void => { const commandHandlerWithTelemetry = (args: any[]): Promise => { - // Send metrics to Segment. - this._telemetryController.trackCommandRun(command); + this._telemetryService.trackCommandRun(command); return commandHandler(args); }; @@ -455,6 +457,7 @@ export default class MDBExtensionController implements vscode.Disposable { EXTENSION_COMMANDS.MDB_OPEN_MONGODB_DOCUMENT_FROM_TREE, (element: DocumentTreeItem): Promise => { return this._editorsController.openMongoDBDocument({ + source: DOCUMENT_SOURCE_TREEVIEW, documentId: element.documentId, namespace: element.namespace, connectionId: this._connectionController.getActiveConnectionId(), @@ -554,7 +557,7 @@ export default class MDBExtensionController implements vscode.Disposable { this._helpExplorer.deactivate(); this._playgroundsExplorer.deactivate(); this._playgroundController.deactivate(); - this._telemetryController.deactivate(); + this._telemetryService.deactivate(); this._languageServerController.deactivate(); this._editorsController.deactivate(); } diff --git a/src/telemetry/index.ts b/src/telemetry/index.ts index b2b9a77a9..7fc815c66 100644 --- a/src/telemetry/index.ts +++ b/src/telemetry/index.ts @@ -1,3 +1,3 @@ -import TelemetryController from './telemetryController'; +import TelemetryService from './telemetryService'; -export { TelemetryController }; +export { TelemetryService }; diff --git a/src/telemetry/telemetryController.ts b/src/telemetry/telemetryService.ts similarity index 73% rename from src/telemetry/telemetryController.ts rename to src/telemetry/telemetryService.ts index 8a131dad3..37661b69d 100644 --- a/src/telemetry/telemetryController.ts +++ b/src/telemetry/telemetryService.ts @@ -1,16 +1,22 @@ import * as vscode from 'vscode'; import { createLogger } from '../logging'; -import SegmentAnalytics = require('analytics-node'); +import SegmentAnalytics from 'analytics-node'; import * as path from 'path'; import { config } from 'dotenv'; import { StorageController } from '../storage'; import { ConnectionTypes } from '../connectionController'; import { getCloudInfo } from 'mongodb-cloud-info'; import { DataServiceType } from '../dataServiceType'; -import type { ExecuteAllResult } from '../utils/types'; +import type { ExecuteAllResult, CloudInfoResult } from '../utils/types'; +import type { InstanceInfoResult } from '../instanceInfoResultType'; +import { ConnectionModelType } from '../connectionModelType'; +import fs from 'fs'; + +export const DOCUMENT_SOURCE_TREEVIEW = 'treeview'; + +export const DOCUMENT_SOURCE_PLAYGROUND = 'playground'; const log = createLogger('telemetry'); -const fs = require('fs'); const ATLAS_REGEX = /mongodb.net[:/]/i; const LOCALHOST_REGEX = /(localhost|127\.0\.0\.1)/i; @@ -23,7 +29,7 @@ type PlaygroundTelemetryEventProperties = { type SegmentProperties = { event: string; - userId?: string; + userId: string; properties?: any; }; @@ -51,9 +57,9 @@ type NewConnectionTelemetryEventProperties = { public_cloud_name?: string | null; is_genuine: boolean; non_genuine_server_name: string | null; - server_version: string; - server_arch: string; - server_os: string; + server_version?: string; + server_arch: string | null; + server_os: string | null; is_used_connect_screen: boolean; is_used_command_palette: boolean; is_used_saved_connection: boolean; @@ -65,12 +71,17 @@ type DocumentUpdatedTelemetryEventProperties = { success: boolean; }; +type DocumentEditedTelemetryEventProperties = { + source: string; +}; + export type TelemetryEventProperties = | PlaygroundTelemetryEventProperties | LinkClickedTelemetryEventProperties | ExtensionCommandRunTelemetryEventProperties | NewConnectionTelemetryEventProperties - | DocumentUpdatedTelemetryEventProperties; + | DocumentUpdatedTelemetryEventProperties + | DocumentEditedTelemetryEventProperties; export enum TelemetryEventTypes { PLAYGROUND_CODE_EXECUTED = 'Playground Code Executed', @@ -79,40 +90,29 @@ export enum TelemetryEventTypes { NEW_CONNECTION = 'New Connection', PLAYGROUND_SAVED = 'Playground Saved', PLAYGROUND_LOADED = 'Playground Loaded', - DOCUMENT_UPDATED = 'Document Updated' + DOCUMENT_UPDATED = 'Document Updated', + DOCUMENT_EDITED = 'Document Edited' } /** * This controller manages telemetry. */ -export default class TelemetryController { - private _shouldTrackTelemetry: boolean; // When tests run the extension, we don't want to track telemetry. - private _segmentAnalytics: SegmentAnalytics; - private _segmentUserID: string | undefined; // The user uuid from the global storage. - private _segmentKey: string | undefined; // The segment API write key. +export default class TelemetryService { + _context: vscode.ExtensionContext; + _shouldTrackTelemetry: boolean; // When tests run the extension, we don't want to track telemetry. + _segmentAnalytics?: SegmentAnalytics; + _segmentUserID: string; // The user uuid from the global storage. + _segmentKey?: string; // The segment API write key. constructor( storageController: StorageController, context: vscode.ExtensionContext, shouldTrackTelemetry?: boolean ) { - this._segmentUserID = storageController.getUserID(); + this._context = context; this._shouldTrackTelemetry = shouldTrackTelemetry || false; - - config({ path: path.join(context.extensionPath, '.env') }); - - try { - const segmentKeyFileLocation = path.join( - context.extensionPath, - './constants.json' - ); - const constants = fs.readFileSync(segmentKeyFileLocation); - - this._segmentKey = JSON.parse(constants)?.segmentKey; - log.info('TELEMETRY key received', typeof this._segmentKey); - } catch (error) { - log.error('TELEMETRY key error', error); - } + this._segmentUserID = storageController.getUserID() || ''; + this._segmentKey = this._readSegmentKey(); vscode.workspace.onDidOpenTextDocument((document) => { if ( @@ -120,25 +120,37 @@ export default class TelemetryController { document.languageId === 'mongodb' && document.uri.scheme === 'file' ) { - // Send metrics to Segment. this.trackPlaygroundLoaded(); } }); vscode.workspace.onDidSaveTextDocument((document) => { if (document && document.languageId === 'mongodb') { - // Send metrics to Segment. this.trackPlaygroundSaved(); } }); } - get segmentUserID(): string | undefined { - return this._segmentUserID; - } + _readSegmentKey(): string | undefined { + config({ path: path.join(this._context.extensionPath, '.env') }); + + try { + const segmentKeyFileLocation = path.join( + this._context.extensionPath, + './constants.json' + ); + // eslint-disable-next-line no-sync + const constantsFile = fs.readFileSync(segmentKeyFileLocation).toString(); + const constants = JSON.parse(constantsFile) as { segmentKey: string }; - get segmentKey(): string | undefined { - return this._segmentKey; + log.info('TELEMETRY key received', typeof constants.segmentKey); + + return constants.segmentKey; + } catch (error) { + log.error('TELEMETRY key error', error); + + return; + } } activateSegmentAnalytics(): void { @@ -153,6 +165,7 @@ export default class TelemetryController { // before flushing the queue automatically. flushInterval: 10000 // 10 seconds is the default libraries' value. }); + this._segmentAnalytics.identify({ userId: this._segmentUserID }); } } @@ -217,7 +230,8 @@ export default class TelemetryController { } try { - const cloudInfo = await getCloudInfo(firstServerHostname); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const cloudInfo: CloudInfoResult = (await getCloudInfo(firstServerHostname)) as CloudInfoResult; if (cloudInfo.isAws) { return { @@ -253,31 +267,34 @@ export default class TelemetryController { dataService: DataServiceType, connectionType: ConnectionTypes ): void { - dataService.instance({}, async (error: any, data: any) => { + dataService.instance({}, async (error: any, data: unknown) => { if (error) { log.error('TELEMETRY data service error', error); } - if (data) { - const firstServerHostname = dataService.client.model.hosts[0].host; + const instanceInfo = data as InstanceInfoResult; + const dataServiceClient = dataService.client as { model: ConnectionModelType}; + + if (instanceInfo) { + const firstServerHostname = dataServiceClient.model.hosts[0].host; const cloudInfo = await this.getCloudInfoFromDataService( firstServerHostname ); - const nonGenuineServerName = data.genuineMongoDB.isGenuine + const nonGenuineServerName = instanceInfo.genuineMongoDB.isGenuine ? null - : data.genuineMongoDB.dbType; + : instanceInfo.genuineMongoDB.dbType; const preparedProperties = { - is_atlas: !!data.client.s.url.match(ATLAS_REGEX), - is_localhost: !!data.client.s.url.match(LOCALHOST_REGEX), - is_data_lake: data.dataLake.isDataLake, - is_enterprise: data.build.enterprise_module, + is_atlas: !!ATLAS_REGEX.exec(instanceInfo.client.s.url), + is_localhost: !!LOCALHOST_REGEX.exec(instanceInfo.client.s.url), + is_data_lake: instanceInfo.dataLake.isDataLake, + is_enterprise: instanceInfo.build.enterprise_module, is_public_cloud: cloudInfo.isPublicCloud, public_cloud_name: cloudInfo.publicCloudName, - is_genuine: data.genuineMongoDB.isGenuine, + is_genuine: instanceInfo.genuineMongoDB.isGenuine, non_genuine_server_name: nonGenuineServerName, - server_version: data.build.version, - server_arch: data.build.raw.buildEnvironment.target_arch, - server_os: data.build.raw.buildEnvironment.target_os, + server_version: instanceInfo.build.version, + server_arch: instanceInfo.build.raw.buildEnvironment.target_arch, + server_os: instanceInfo.build.raw.buildEnvironment.target_os, is_used_connect_screen: connectionType === ConnectionTypes.CONNECTION_FORM, is_used_command_palette: @@ -352,4 +369,8 @@ export default class TelemetryController { trackDocumentUpdated(source: string, success: boolean): void { this.track(TelemetryEventTypes.DOCUMENT_UPDATED, { source, success }); } + + trackOpenMongoDBDocumentFromPlayground(source: string): void { + this.track(TelemetryEventTypes.DOCUMENT_EDITED, { source }); + } } diff --git a/src/test/suite/connectionController.test.ts b/src/test/suite/connectionController.test.ts index a9e17daca..618ed758c 100644 --- a/src/test/suite/connectionController.test.ts +++ b/src/test/suite/connectionController.test.ts @@ -5,7 +5,7 @@ import * as sinon from 'sinon'; import Connection from 'mongodb-connection-model/lib/model'; import DataService from 'mongodb-data-service'; -import TelemetryController from '../../telemetry/telemetryController'; +import TelemetryService from '../../telemetry/telemetryService'; import ConnectionController, { DataServiceEventTypes } from '../../connectionController'; @@ -43,14 +43,14 @@ suite('Connection Controller Test Suite', function () { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(mockExtensionContext), mockStorageController, - testTelemetryController + testTelemetryService ); beforeEach(() => { diff --git a/src/test/suite/editors/activeDBCodeLensProvider.test.ts b/src/test/suite/editors/activeDBCodeLensProvider.test.ts index 4e9de174e..81955b447 100644 --- a/src/test/suite/editors/activeDBCodeLensProvider.test.ts +++ b/src/test/suite/editors/activeDBCodeLensProvider.test.ts @@ -4,7 +4,7 @@ import { StatusView } from '../../../views'; import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; import { TestExtensionContext } from '../stubs'; import { StorageController } from '../../../storage'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import { beforeEach, afterEach } from 'mocha'; const sinon = require('sinon'); @@ -14,7 +14,7 @@ const expect = chai.expect; suite('Active DB CodeLens Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); @@ -24,7 +24,7 @@ suite('Active DB CodeLens Provider Test Suite', () => { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); const testCodeLensProvider = new ActiveDBCodeLensProvider( testConnectionController @@ -56,7 +56,7 @@ suite('Active DB CodeLens Provider Test Suite', () => { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); const testCodeLensProvider = new ActiveDBCodeLensProvider( testConnectionController diff --git a/src/test/suite/editors/collectionDocumentsProvider.test.ts b/src/test/suite/editors/collectionDocumentsProvider.test.ts index e5abd6042..35b9671ac 100644 --- a/src/test/suite/editors/collectionDocumentsProvider.test.ts +++ b/src/test/suite/editors/collectionDocumentsProvider.test.ts @@ -9,7 +9,7 @@ import { StatusView } from '../../../views'; import { TestExtensionContext } from '../stubs'; import { StorageController } from '../../../storage'; import CollectionDocumentsOperationsStore from '../../../editors/collectionDocumentsOperationsStore'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; const mockDocumentsAsJsonString = `[ { @@ -46,14 +46,14 @@ suite('Collection Documents Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); const mockConnectionController = new ConnectionController( new StatusView(mockExtensionContext), mockStorageController, - testTelemetryController + testTelemetryService ); mockConnectionController.setActiveConnection(mockActiveConnection); @@ -102,14 +102,14 @@ suite('Collection Documents Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); const mockConnectionController = new ConnectionController( new StatusView(mockExtensionContext), mockStorageController, - testTelemetryController + testTelemetryService ); mockConnectionController.setActiveConnection(mockActiveConnection); @@ -147,14 +147,14 @@ suite('Collection Documents Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); const mockConnectionController = new ConnectionController( new StatusView(mockExtensionContext), mockStorageController, - testTelemetryController + testTelemetryService ); mockConnectionController.setActiveConnection(mockActiveConnection); @@ -200,14 +200,14 @@ suite('Collection Documents Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); const mockConnectionController = new ConnectionController( new StatusView(mockExtensionContext), mockStorageController, - testTelemetryController + testTelemetryService ); mockConnectionController.setActiveConnection(mockActiveConnection); diff --git a/src/test/suite/editors/editDocumentCodeLensProvider.test.ts b/src/test/suite/editors/editDocumentCodeLensProvider.test.ts index b667c1498..8156c0a7c 100644 --- a/src/test/suite/editors/editDocumentCodeLensProvider.test.ts +++ b/src/test/suite/editors/editDocumentCodeLensProvider.test.ts @@ -3,13 +3,13 @@ import EditDocumentCodeLensProvider from '../../../editors/editDocumentCodeLensP import ConnectionController from '../../../connectionController'; import { TestExtensionContext } from '../stubs'; import { StorageController } from '../../../storage'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import { StatusView } from '../../../views'; suite('Edit Document Code Lens Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); @@ -17,7 +17,7 @@ suite('Edit Document Code Lens Provider Test Suite', () => { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); test('provideCodeLenses returns an empty array if codeLensesInfo is empty', () => { @@ -30,131 +30,139 @@ suite('Edit Document Code Lens Provider Test Suite', () => { assert(codeLens.length === 0); }); - test('provideCodeLenses returns one code lens when result is a single document', () => { - const testCodeLensProvider = new EditDocumentCodeLensProvider( - testConnectionController - ); + suite('after updateCodeLensesForPlayground', () => { + test('provideCodeLenses returns one code lens when result is a single document', () => { + const testCodeLensProvider = new EditDocumentCodeLensProvider( + testConnectionController + ); - testCodeLensProvider.updateCodeLensesPosition({ - namespace: 'db.coll', - type: 'Document', - content: { _id: '93333a0d-83f6-4e6f-a575-af7ea6187a4a' } - }); + testCodeLensProvider.updateCodeLensesForPlayground({ + namespace: 'db.coll', + type: 'Document', + content: { _id: '93333a0d-83f6-4e6f-a575-af7ea6187a4a' } + }); - const codeLens = testCodeLensProvider.provideCodeLenses(); + const codeLens = testCodeLensProvider.provideCodeLenses(); - assert(!!codeLens); - assert(codeLens.length === 1); - const range = codeLens[0].range; - const expectedStartLine = 1; - assert( - range.start.line === expectedStartLine, - `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}` - ); - const expectedEnd = 1; - assert( - range.end.line === expectedEnd, - `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}` - ); - }); + assert(!!codeLens); + assert(codeLens.length === 1); + const range = codeLens[0].range; + const expectedStartLine = 1; + assert( + range.start.line === expectedStartLine, + `Expected a codeLens position to be at line ${expectedStartLine}, found ${range.start.line}` + ); + const expectedEnd = 1; + assert( + range.end.line === expectedEnd, + `Expected a codeLens position to be at line ${expectedEnd}, found ${range.end.line}` + ); + assert(codeLens[0].command?.title === 'Edit Document'); + assert(!!codeLens[0].command?.command); + assert(codeLens[0].command?.command === 'mdb.openMongoDBDocumentFromPlayground'); + const commandArguments = codeLens[0].command?.arguments; + assert(!!commandArguments); + assert(commandArguments[0].source === 'playground'); + }); - test('provideCodeLenses returns two code lenses when result is array of two documents', () => { - const testCodeLensProvider = new EditDocumentCodeLensProvider( - testConnectionController - ); + test('provideCodeLenses returns two code lenses when result is array of two documents', () => { + const testCodeLensProvider = new EditDocumentCodeLensProvider( + testConnectionController + ); - testCodeLensProvider.updateCodeLensesPosition({ - namespace: 'db.coll', - type: 'Cursor', - content: [ - { _id: '93333a0d-83f6-4e6f-a575-af7ea6187a4a' }, - { _id: '21333a0d-83f6-4e6f-a575-af7ea6187444' } - ] - }); + testCodeLensProvider.updateCodeLensesForPlayground({ + namespace: 'db.coll', + type: 'Cursor', + content: [ + { _id: '93333a0d-83f6-4e6f-a575-af7ea6187a4a' }, + { _id: '21333a0d-83f6-4e6f-a575-af7ea6187444' } + ] + }); - const codeLens = testCodeLensProvider.provideCodeLenses(); + const codeLens = testCodeLensProvider.provideCodeLenses(); - assert(!!codeLens); - assert(codeLens.length === 2); - const firstRange = codeLens[0].range; - const firstExpectedStartLine = 2; - assert( - firstRange.start.line === firstExpectedStartLine, - `Expected a codeLens position to be at line ${firstExpectedStartLine}, found ${firstRange.start.line}` - ); - const firstExpectedEnd = 2; - assert( - firstRange.end.line === firstExpectedEnd, - `Expected a codeLens position to be at line ${firstExpectedEnd}, found ${firstRange.end.line}` - ); - const secondRange = codeLens[1].range; - const secondExpectedStartLine = 5; - assert( - secondRange.start.line === secondExpectedStartLine, - `Expected a codeLens position to be at line ${secondExpectedStartLine}, found ${secondRange.start.line}` - ); - const secondExpectedEnd = 5; - assert( - secondRange.end.line === secondExpectedEnd, - `Expected a codeLens position to be at line ${secondExpectedEnd}, found ${secondRange.end.line}` - ); - }); + assert(!!codeLens); + assert(codeLens.length === 2); + const firstRange = codeLens[0].range; + const firstExpectedStartLine = 2; + assert( + firstRange.start.line === firstExpectedStartLine, + `Expected a codeLens position to be at line ${firstExpectedStartLine}, found ${firstRange.start.line}` + ); + const firstExpectedEnd = 2; + assert( + firstRange.end.line === firstExpectedEnd, + `Expected a codeLens position to be at line ${firstExpectedEnd}, found ${firstRange.end.line}` + ); + const secondRange = codeLens[1].range; + const secondExpectedStartLine = 5; + assert( + secondRange.start.line === secondExpectedStartLine, + `Expected a codeLens position to be at line ${secondExpectedStartLine}, found ${secondRange.start.line}` + ); + const secondExpectedEnd = 5; + assert( + secondRange.end.line === secondExpectedEnd, + `Expected a codeLens position to be at line ${secondExpectedEnd}, found ${secondRange.end.line}` + ); + }); - test('provideCodeLenses returns code lenses when result is ejson array', () => { - const testCodeLensProvider = new EditDocumentCodeLensProvider( - testConnectionController - ); + test('provideCodeLenses returns code lenses when result is ejson array', () => { + const testCodeLensProvider = new EditDocumentCodeLensProvider( + testConnectionController + ); - testCodeLensProvider.updateCodeLensesPosition({ - namespace: 'db.coll', - type: 'Cursor', - content: [ - { - _id: 4, - item: 'xyz', - price: 5, - quantity: 20, - date: { - $date: '2014-04-04T11:21:39.736Z' - } - }, - { - _id: 5, - item: 'abc', - price: 10, - quantity: 10, - date: { - $date: '2014-04-04T21:23:13.331Z' + testCodeLensProvider.updateCodeLensesForPlayground({ + namespace: 'db.coll', + type: 'Cursor', + content: [ + { + _id: 4, + item: 'xyz', + price: 5, + quantity: 20, + date: { + $date: '2014-04-04T11:21:39.736Z' + } + }, + { + _id: 5, + item: 'abc', + price: 10, + quantity: 10, + date: { + $date: '2014-04-04T21:23:13.331Z' + } } - } - ] - }); + ] + }); - const codeLens = testCodeLensProvider.provideCodeLenses(); + const codeLens = testCodeLensProvider.provideCodeLenses(); - assert(!!codeLens); - assert(codeLens.length === 2); - const firstRange = codeLens[0].range; - const firstExpectedStartLine = 2; - assert( - firstRange.start.line === firstExpectedStartLine, - `Expected a codeLens position to be at line ${firstExpectedStartLine}, found ${firstRange.start.line}` - ); - const firstExpectedEnd = 2; - assert( - firstRange.end.line === firstExpectedEnd, - `Expected a codeLens position to be at line ${firstExpectedEnd}, found ${firstRange.end.line}` - ); - const secondRange = codeLens[1].range; - const secondExpectedStartLine = 11; - assert( - secondRange.start.line === secondExpectedStartLine, - `Expected a codeLens position to be at line ${secondExpectedStartLine}, found ${secondRange.start.line}` - ); - const secondExpectedEnd = 11; - assert( - secondRange.end.line === secondExpectedEnd, - `Expected a codeLens position to be at line ${secondExpectedEnd}, found ${secondRange.end.line}` - ); + assert(!!codeLens); + assert(codeLens.length === 2); + const firstRange = codeLens[0].range; + const firstExpectedStartLine = 2; + assert( + firstRange.start.line === firstExpectedStartLine, + `Expected a codeLens position to be at line ${firstExpectedStartLine}, found ${firstRange.start.line}` + ); + const firstExpectedEnd = 2; + assert( + firstRange.end.line === firstExpectedEnd, + `Expected a codeLens position to be at line ${firstExpectedEnd}, found ${firstRange.end.line}` + ); + const secondRange = codeLens[1].range; + const secondExpectedStartLine = 11; + assert( + secondRange.start.line === secondExpectedStartLine, + `Expected a codeLens position to be at line ${secondExpectedStartLine}, found ${secondRange.start.line}` + ); + const secondExpectedEnd = 11; + assert( + secondRange.end.line === secondExpectedEnd, + `Expected a codeLens position to be at line ${secondExpectedEnd}, found ${secondRange.end.line}` + ); + }); }); }); diff --git a/src/test/suite/editors/mongoDBDocumentService.test.ts b/src/test/suite/editors/mongoDBDocumentService.test.ts index 6cc8b52ef..1e8d4f9ef 100644 --- a/src/test/suite/editors/mongoDBDocumentService.test.ts +++ b/src/test/suite/editors/mongoDBDocumentService.test.ts @@ -5,7 +5,7 @@ import ConnectionController from '../../../connectionController'; import { TestExtensionContext } from '../stubs'; import { StorageController } from '../../../storage'; import { StatusView } from '../../../views'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import { afterEach } from 'mocha'; import { EJSON } from 'bson'; @@ -18,21 +18,21 @@ suite('MongoDB Document Service Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(mockExtensionContext); const testStatusView = new StatusView(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, mockExtensionContext ); const testConnectionController = new ConnectionController( testStatusView, testStorageController, - testTelemetryController + testTelemetryService ); const testMongoDBDocumentService = new MongoDBDocumentService( mockExtensionContext, testDocumentIdStore, testConnectionController, testStatusView, - testTelemetryController + testTelemetryService ); const sandbox = sinon.createSandbox(); @@ -48,6 +48,7 @@ suite('MongoDB Document Service Test Suite', () => { const documentId = '93333a0d-83f6-4e6f-a575-af7ea6187a4a'; const document: EJSON.SerializableTypes = { _id: '123' }; const newDocument = { _id: '123', price: 5000 }; + const source = 'treeview'; const mockActiveConnectionId = sinon.fake.returns('tasty_sandwhich'); sinon.replace( @@ -86,7 +87,8 @@ suite('MongoDB Document Service Test Suite', () => { namespace, documentId, connectionId, - newDocument + newDocument, + source }); expect(document).to.be.deep.equal(newDocument); @@ -98,6 +100,7 @@ suite('MongoDB Document Service Test Suite', () => { const documentId = '93333a0d-83f6-4e6f-a575-af7ea6187a4a'; const line = 1; const documents = [{ _id: '123' }]; + const source = 'playground'; const mockGetActiveDataService = sinon.fake.returns({ find: ( @@ -132,7 +135,8 @@ suite('MongoDB Document Service Test Suite', () => { namespace, documentId, line, - connectionId + connectionId, + source }); expect(result).to.be.deep.equal(JSON.parse(EJSON.stringify(documents[0]))); @@ -143,6 +147,7 @@ suite('MongoDB Document Service Test Suite', () => { const connectionId = 'tasty_sandwhich'; const documentId = '93333a0d-83f6-4e6f-a575-af7ea6187a4a'; const newDocument = { _id: '123', price: 5000 }; + const source = 'playground'; const fakeVscodeErrorMessage = sinon.fake(); sinon.replace(vscode.window, 'showErrorMessage', fakeVscodeErrorMessage); @@ -166,7 +171,8 @@ suite('MongoDB Document Service Test Suite', () => { documentId, namespace, connectionId, - newDocument + newDocument, + source }); } catch (error) { const expectedMessage = @@ -181,6 +187,7 @@ suite('MongoDB Document Service Test Suite', () => { const connectionId = 'tasty_sandwhich'; const documentId = '93333a0d-83f6-4e6f-a575-af7ea6187a4a'; const newDocument = { _id: '123', price: 5000 }; + const source = 'playground'; const fakeVscodeErrorMessage = sinon.fake(); sinon.replace(vscode.window, 'showErrorMessage', fakeVscodeErrorMessage); @@ -204,7 +211,8 @@ suite('MongoDB Document Service Test Suite', () => { documentId, namespace, connectionId, - newDocument + newDocument, + source }); } catch (error) { const expectedMessage = @@ -219,6 +227,7 @@ suite('MongoDB Document Service Test Suite', () => { const connectionId = '123'; const documentId = '93333a0d-83f6-4e6f-a575-af7ea6187a4a'; const line = 1; + const source = 'playground'; const mockGetActiveConnectionId = sinon.fake.returns('345'); sinon.replace( @@ -245,7 +254,8 @@ suite('MongoDB Document Service Test Suite', () => { namespace, documentId, line, - connectionId + connectionId, + source }); } catch (error) { const expectedMessage = diff --git a/src/test/suite/editors/playgroundController.test.ts b/src/test/suite/editors/playgroundController.test.ts index 177514ef1..df93d577a 100644 --- a/src/test/suite/editors/playgroundController.test.ts +++ b/src/test/suite/editors/playgroundController.test.ts @@ -6,7 +6,7 @@ import { StatusView } from '../../../views'; import { StorageController } from '../../../storage'; import { TestExtensionContext, MockLanguageServerController } from '../stubs'; import { before, beforeEach, afterEach } from 'mocha'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import PlaygroundResultProvider from '../../../editors/playgroundResultProvider'; import ActiveDBCodeLensProvider from '../../../editors/activeConnectionCodeLensProvider'; import PartialExecutionCodeLensProvider from '../../../editors/partialExecutionCodeLensProvider'; @@ -30,7 +30,7 @@ suite('Playground Controller Test Suite', function () { mockExtensionContext.extensionPath = '../../'; const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); @@ -38,7 +38,7 @@ suite('Playground Controller Test Suite', function () { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); const mockLanguageServerController = new MockLanguageServerController( mockExtensionContext, @@ -56,7 +56,7 @@ suite('Playground Controller Test Suite', function () { mockExtensionContext, testConnectionController, mockLanguageServerController as LanguageServerController, - testTelemetryController, + testTelemetryService, testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, @@ -374,7 +374,7 @@ suite('Playground Controller Test Suite', function () { mockExtensionContext, testConnectionController, mockLanguageServerController as LanguageServerController, - testTelemetryController, + testTelemetryService, testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, diff --git a/src/test/suite/editors/playgroundResultProvider.test.ts b/src/test/suite/editors/playgroundResultProvider.test.ts index 62d78487a..fbe997532 100644 --- a/src/test/suite/editors/playgroundResultProvider.test.ts +++ b/src/test/suite/editors/playgroundResultProvider.test.ts @@ -3,7 +3,7 @@ import { TestExtensionContext } from '../stubs'; import { afterEach } from 'mocha'; import ConnectionController from '../../../connectionController'; import { StorageController } from '../../../storage'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import { StatusView } from '../../../views'; const sinon = require('sinon'); @@ -13,7 +13,7 @@ const expect = chai.expect; suite('Playground Result Provider Test Suite', () => { const mockExtensionContext = new TestExtensionContext(); const mockStorageController = new StorageController(mockExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); @@ -21,7 +21,7 @@ suite('Playground Result Provider Test Suite', () => { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); afterEach(() => { @@ -207,7 +207,7 @@ suite('Playground Result Provider Test Suite', () => { const mockRefresh = sinon.fake.resolves(); sinon.replace( testPlaygroundResultViewProvider._editDocumentCodeLensProvider, - 'updateCodeLensesPosition', + 'updateCodeLensesForPlayground', mockRefresh ); @@ -238,7 +238,7 @@ suite('Playground Result Provider Test Suite', () => { const mockRefresh = sinon.fake.resolves(); sinon.replace( testPlaygroundResultViewProvider._editDocumentCodeLensProvider, - 'updateCodeLensesPosition', + 'updateCodeLensesForPlayground', mockRefresh ); diff --git a/src/test/suite/explorer/helpExplorer.test.ts b/src/test/suite/explorer/helpExplorer.test.ts index 67b187ea4..78b434f84 100644 --- a/src/test/suite/explorer/helpExplorer.test.ts +++ b/src/test/suite/explorer/helpExplorer.test.ts @@ -17,7 +17,7 @@ suite('Help Explorer Test Suite', function () { const testHelpExplorer = new HelpExplorer(); assert(testHelpExplorer._treeView === undefined); testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); assert(testHelpExplorer._treeView !== undefined); }); @@ -26,7 +26,7 @@ suite('Help Explorer Test Suite', function () { const testHelpExplorer = mdbTestExtension.testExtensionController._helpExplorer; testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); const helpTreeItems = await testHelpExplorer._treeController.getChildren(); assert(helpTreeItems.length === 6); @@ -36,7 +36,7 @@ suite('Help Explorer Test Suite', function () { const testHelpExplorer = mdbTestExtension.testExtensionController._helpExplorer; testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); const helpTreeItems = await testHelpExplorer._treeController.getChildren(); const atlasHelpItem = helpTreeItems[5]; @@ -56,7 +56,7 @@ suite('Help Explorer Test Suite', function () { mdbTestExtension.testExtensionController._helpExplorer; testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); const stubExecuteCommand = sinon.fake.resolves(); @@ -65,7 +65,7 @@ suite('Help Explorer Test Suite', function () { const atlasHelpItem = helpTreeItems[1]; testHelpExplorer._treeController.onClickHelpItem( atlasHelpItem, - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); assert(stubExecuteCommand.called); assert(stubExecuteCommand.firstCall.args[0] === 'vscode.open'); @@ -84,7 +84,7 @@ suite('Help Explorer Test Suite', function () { mdbTestExtension.testExtensionController._helpExplorer; testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); const stubExecuteCommand = sinon.fake.resolves(); @@ -93,7 +93,7 @@ suite('Help Explorer Test Suite', function () { const atlasHelpItem = helpTreeItems[5]; testHelpExplorer._treeController.onClickHelpItem( atlasHelpItem, - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); assert(stubExecuteCommand.called); assert(stubExecuteCommand.firstCall.args[0] === atlasHelpItem.url); @@ -105,12 +105,12 @@ suite('Help Explorer Test Suite', function () { const stubLinkClickedTelemetry = sinon.fake.resolves(); sinon.replace( - mdbTestExtension.testExtensionController._telemetryController, + mdbTestExtension.testExtensionController._telemetryService, 'trackLinkClicked', stubLinkClickedTelemetry ); testHelpExplorer.activateHelpTreeView( - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); sinon.replace(vscode.commands, 'executeCommand', sinon.fake.resolves()); @@ -118,7 +118,7 @@ suite('Help Explorer Test Suite', function () { const atlasHelpItem = helpTreeItems[5]; testHelpExplorer._treeController.onClickHelpItem( atlasHelpItem, - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); assert(stubLinkClickedTelemetry.called); assert(stubLinkClickedTelemetry.firstCall.args[0] === 'helpPanel'); diff --git a/src/test/suite/language/languageServerController.test.ts b/src/test/suite/language/languageServerController.test.ts index 71f2495f4..d103b8c94 100644 --- a/src/test/suite/language/languageServerController.test.ts +++ b/src/test/suite/language/languageServerController.test.ts @@ -1,5 +1,5 @@ import { before, after } from 'mocha'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import path from 'path'; import fs from 'fs'; @@ -34,7 +34,7 @@ suite('Language Server Controller Test Suite', () => { const testLanguageServerController = new LanguageServerController( mockExtensionContext ); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( mockStorageController, mockExtensionContext ); @@ -42,7 +42,7 @@ suite('Language Server Controller Test Suite', () => { const testConnectionController = new ConnectionController( testStatusView, mockStorageController, - testTelemetryController + testTelemetryService ); const testPlaygroundResultProvider = new PlaygroundResultProvider( mockExtensionContext, @@ -56,7 +56,7 @@ suite('Language Server Controller Test Suite', () => { mockExtensionContext, testConnectionController, testLanguageServerController, - testTelemetryController, + testTelemetryService, testStatusView, testPlaygroundResultProvider, testActiveDBCodeLensProvider, diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index f716b1b11..843aac7dd 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -1258,7 +1258,8 @@ suite('MDBExtensionController Test Suite', function () { query: [ 'namespace=waffle.house', 'connectionId=tasty_sandwhich', - 'documentId=93333a0d-83f6-4e6f-a575-af7ea6187a4a' + 'documentId=93333a0d-83f6-4e6f-a575-af7ea6187a4a', + 'source=treeview' ].join('&') }, getText: () => JSON.stringify(mockDocument), @@ -1311,6 +1312,7 @@ suite('MDBExtensionController Test Suite', function () { assert(mockOpenTextDocument.firstArg.scheme === 'VIEW_DOCUMENT_SCHEME'); assert(mockOpenTextDocument.firstArg.query.includes('documentId=')); assert(mockOpenTextDocument.firstArg.query.includes('connectionId=')); + assert(mockOpenTextDocument.firstArg.query.includes('source=treeview')); assert( mockOpenTextDocument.firstArg.query.includes('namespace=waffle.house') ); @@ -1333,6 +1335,151 @@ suite('MDBExtensionController Test Suite', function () { ); }); + test('document opened from a tree has treeview sorce', async () => { + const mockDocument = { + _id: 'pancakes', + name: '', + time: { + $time: '12345' + } + }; + const documentItem = new DocumentTreeItem(mockDocument, 'waffle.house', 0); + + const mockFetchDocument = sinon.fake.resolves(null); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController._mongoDBDocumentService, + 'fetchDocument', + mockFetchDocument + ); + + await vscode.commands.executeCommand( + 'mdb.openMongoDBDocumentFromTree', + documentItem + ); + + assert(mockFetchDocument.firstArg.source === 'treeview'); + }); + + test('document opened from playground results has treeview sorce', async () => { + const documentItem = { + source: 'playground', + line: 1, + documentId: '93333a0d-83f6-4e6f-a575-af7ea6187a4a', + namespace: 'db.coll', + connectionId: null + }; + + const mockFetchDocument = sinon.fake.resolves(null); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController._mongoDBDocumentService, + 'fetchDocument', + mockFetchDocument + ); + + await vscode.commands.executeCommand( + 'mdb.openMongoDBDocumentFromPlayground', + documentItem + ); + + assert(mockFetchDocument.firstArg.source === 'playground'); + }); + + test('fetchDocument recieves treeview source if document opened from tree', async () => { + const mockDocument = { + _id: 'pancakes', + name: '', + time: { + $time: '12345' + } + }; + + const mockShowTextDocument = sinon.fake.resolves(); + sinon.replace(vscode.window, 'showTextDocument', mockShowTextDocument); + + const mockGet = sinon.fake.returns('pancakes'); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController + ._documentIdStore, + 'get', + mockGet + ); + + sandbox.replaceGetter(vscode.window, 'activeTextEditor', () => ({ + document: { + uri: { + scheme: 'VIEW_DOCUMENT_SCHEME', + query: [ + 'namespace=waffle.house', + 'connectionId=tasty_sandwhich', + 'documentId=93333a0d-83f6-4e6f-a575-af7ea6187a4a', + 'source=treeview' + ].join('&') + }, + getText: () => JSON.stringify(mockDocument), + save: () => {} + } + })); + + const mockReplaceDocument = sinon.fake.resolves(null); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController._mongoDBDocumentService, + 'replaceDocument', + mockReplaceDocument + ); + + await vscode.commands.executeCommand('mdb.saveMongoDBDocument'); + + assert(mockReplaceDocument.firstArg.source === 'treeview'); + }); + + test('fetchDocument recieves playground source if document opened from playground results', async () => { + const mockDocument = { + _id: 'pancakes', + name: '', + time: { + $time: '12345' + } + }; + + const mockShowTextDocument = sinon.fake.resolves(); + sinon.replace(vscode.window, 'showTextDocument', mockShowTextDocument); + + const mockGet = sinon.fake.returns('pancakes'); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController + ._documentIdStore, + 'get', + mockGet + ); + + sandbox.replaceGetter(vscode.window, 'activeTextEditor', () => ({ + document: { + uri: { + scheme: 'VIEW_DOCUMENT_SCHEME', + query: [ + 'namespace=waffle.house', + 'connectionId=tasty_sandwhich', + 'documentId=93333a0d-83f6-4e6f-a575-af7ea6187a4a', + 'source=playground' + ].join('&') + }, + getText: () => JSON.stringify(mockDocument), + save: () => {} + } + })); + + const mockReplaceDocument = sinon.fake.resolves(null); + sinon.replace( + mdbTestExtension.testExtensionController._editorsController._mongoDBDocumentService, + 'replaceDocument', + mockReplaceDocument + ); + + await vscode.commands.executeCommand('mdb.saveMongoDBDocument'); + + assert(mockReplaceDocument.firstArg.source === 'playground'); + }); + test('mdb.searchForDocuments should create a MongoDB playground with search template', async () => { const mockOpenTextDocument = sinon.fake.resolves('untitled'); sinon.replace(vscode.workspace, 'openTextDocument', mockOpenTextDocument); diff --git a/src/test/suite/telemetry/telemetryController.test.ts b/src/test/suite/telemetry/telemetryController.test.ts index da27be45d..a2f5f9560 100644 --- a/src/test/suite/telemetry/telemetryController.test.ts +++ b/src/test/suite/telemetry/telemetryController.test.ts @@ -26,8 +26,8 @@ suite('Telemetry Controller Test Suite', () => { port: 27018 }) ); - const testTelemetryController = - mdbTestExtension.testExtensionController._telemetryController; + const testTelemetryService = + mdbTestExtension.testExtensionController._telemetryService; let mockTrackNewConnection: Promise; let mockTrackCommandRun: Promise; @@ -43,22 +43,22 @@ suite('Telemetry Controller Test Suite', () => { mockTrack = sinon.fake.resolves(); sinon.replace( - mdbTestExtension.testExtensionController._telemetryController, + mdbTestExtension.testExtensionController._telemetryService, 'trackNewConnection', mockTrackNewConnection ); sinon.replace( - mdbTestExtension.testExtensionController._telemetryController, + mdbTestExtension.testExtensionController._telemetryService, 'trackCommandRun', mockTrackCommandRun ); sinon.replace( - mdbTestExtension.testExtensionController._telemetryController, + mdbTestExtension.testExtensionController._telemetryService, 'trackPlaygroundCodeExecuted', mockTrackPlaygroundCodeExecuted ); sinon.replace( - mdbTestExtension.testExtensionController._telemetryController, + mdbTestExtension.testExtensionController._telemetryService, 'trackPlaygroundLoaded', mockTrackPlaygroundLoadedMethod ); @@ -67,7 +67,7 @@ suite('Telemetry Controller Test Suite', () => { 'executeAll', sinon.fake.resolves([{ type: 'TEST', content: 'Result' }]) ); - sinon.replace(testTelemetryController, 'track', mockTrack); + sinon.replace(testTelemetryService, 'track', mockTrack); }); afterEach(() => { @@ -85,8 +85,8 @@ suite('Telemetry Controller Test Suite', () => { } expect(segmentKey).to.be.equal(process.env.SEGMENT_KEY); - expect(testTelemetryController.segmentKey).to.be.a('string'); - expect(testTelemetryController.segmentUserID).to.be.a('string'); + expect(testTelemetryService._segmentKey).to.be.a('string'); + expect(testTelemetryService._segmentUserID).to.be.a('string'); }); test('track command run event', (done) => { @@ -147,7 +147,7 @@ suite('Telemetry Controller Test Suite', () => { }); test('track document saved form a tree-view event', () => { - testTelemetryController.trackDocumentUpdated('treeview', true); + testTelemetryService.trackDocumentUpdated('treeview', true); sinon.assert.calledWith( mockTrack, @@ -156,6 +156,28 @@ suite('Telemetry Controller Test Suite', () => { ); }); + test('track document opened form playground results', async () => { + const mockTrackOpenMongoDBDocumentFromPlayground = sinon.fake.resolves(); + sinon.replace( + mdbTestExtension.testExtensionController._telemetryService, + 'trackOpenMongoDBDocumentFromPlayground', + mockTrackOpenMongoDBDocumentFromPlayground + ); + + await vscode.commands.executeCommand( + 'mdb.openMongoDBDocumentFromPlayground', + { + source: 'playground', + line: 1, + documentId: '93333a0d-83f6-4e6f-a575-af7ea6187a4a', + namespace: 'db.coll', + connectionId: null + } + ); + + expect(mockTrackOpenMongoDBDocumentFromPlayground.firstArg).to.be.equal('playground'); + }); + test('track playground code executed event', async () => { const mockPlaygroundController = mdbTestExtension.testExtensionController._playgroundController; @@ -177,7 +199,7 @@ suite('Telemetry Controller Test Suite', () => { }); test('track playground saved event', () => { - testTelemetryController.trackPlaygroundSaved(); + testTelemetryService.trackPlaygroundSaved(); sinon.assert.calledWith(mockTrack); }); @@ -188,7 +210,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'AggregationCursor', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('aggregation'); }); @@ -198,7 +220,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'BulkWriteResult', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -208,7 +230,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'Collection', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -218,7 +240,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'Cursor', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('query'); }); @@ -228,7 +250,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'Database', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -238,7 +260,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'DeleteResult', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('delete'); }); @@ -248,7 +270,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'InsertManyResult', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('insert'); }); @@ -258,7 +280,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'InsertOneResult', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('insert'); }); @@ -268,7 +290,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'ReplicaSet', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -278,7 +300,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'Shard', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -288,7 +310,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'ShellApi', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); @@ -298,7 +320,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: 'UpdateResult', content: '' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('update'); }); @@ -308,7 +330,7 @@ suite('Telemetry Controller Test Suite', () => { outputLines: [], result: { namespace: null, type: null, content: '2' } }; - const type = testTelemetryController.getPlaygroundResultType(res); + const type = testTelemetryService.getPlaygroundResultType(res); expect(type).to.deep.equal('other'); }); diff --git a/src/test/suite/views/webviewController.test.ts b/src/test/suite/views/webviewController.test.ts index 72176c6ad..d05a7c3d1 100644 --- a/src/test/suite/views/webviewController.test.ts +++ b/src/test/suite/views/webviewController.test.ts @@ -5,7 +5,7 @@ import * as sinon from 'sinon'; import Connection = require('mongodb-connection-model/lib/model'); import * as linkHelper from '../../../utils/linkHelper'; -import TelemetryController from '../../../telemetry/telemetryController'; +import TelemetryService from '../../../telemetry/telemetryService'; import ConnectionController from '../../../connectionController'; import { StorageController } from '../../../storage'; import WebviewController, { @@ -44,7 +44,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( mdbTestExtension.testExtensionController._connectionController, - mdbTestExtension.testExtensionController._telemetryController + mdbTestExtension.testExtensionController._telemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -91,14 +91,14 @@ suite('Webview Test Suite', () => { test('web view listens for a connect message and adds the connection', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceivedSet = false; let messageReceived; @@ -135,7 +135,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -165,14 +165,14 @@ suite('Webview Test Suite', () => { test('web view sends a successful connect result on a successful connection', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceivedSet = false; let messageReceived; @@ -207,7 +207,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -237,14 +237,14 @@ suite('Webview Test Suite', () => { test('web view sends an unsuccessful connect result on an unsuccessful connection', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -273,7 +273,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -291,14 +291,14 @@ suite('Webview Test Suite', () => { test('web view sends an unsuccessful connect result on an attempt that is overridden', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -331,7 +331,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -360,14 +360,14 @@ suite('Webview Test Suite', () => { test('web view opens file picker on file picker request', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); const fakeVSCodeOpenDialog = sinon.fake.resolves({ path: '/somefilepath/test.text' @@ -402,7 +402,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -417,14 +417,14 @@ suite('Webview Test Suite', () => { test('web view returns file name on file picker request', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -461,7 +461,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -475,14 +475,14 @@ suite('Webview Test Suite', () => { test('web view runs the "connectWithURI" command when open connection string input is recieved', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -508,7 +508,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -530,14 +530,14 @@ suite('Webview Test Suite', () => { test('webview returns the connection status on a connection status request', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -566,7 +566,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -580,14 +580,14 @@ suite('Webview Test Suite', () => { test('webview returns the connection status on a connection status request', (done) => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -617,7 +617,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -635,14 +635,14 @@ suite('Webview Test Suite', () => { test('calls to rename the active connection when a rename active connection message is passed', async () => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); const testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); let messageReceived; const fakeWebview = { @@ -673,7 +673,7 @@ suite('Webview Test Suite', () => { const testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); @@ -699,7 +699,7 @@ suite('Webview Test Suite', () => { suite('with a rendered webview', () => { const testExtensionContext = new TestExtensionContext(); const testStorageController = new StorageController(testExtensionContext); - const testTelemetryController = new TelemetryController( + const testTelemetryService = new TelemetryService( testStorageController, testExtensionContext ); @@ -714,7 +714,7 @@ suite('Webview Test Suite', () => { testConnectionController = new ConnectionController( new StatusView(testExtensionContext), testStorageController, - testTelemetryController + testTelemetryService ); fakeWebview = { @@ -737,7 +737,7 @@ suite('Webview Test Suite', () => { testWebviewController = new WebviewController( testConnectionController, - testTelemetryController + testTelemetryService ); testWebviewController.openWebview(mdbTestExtension.testExtensionContext); diff --git a/src/utils/types.ts b/src/utils/types.ts index 04bf56539..be9c550c0 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -12,8 +12,15 @@ export type ExecuteAllResult = { }; export type ResultCodeLensInfo = { + source: string; line: number; documentId: EJSON.SerializableTypes; namespace: string; connectionId: string | null; }; + +export type CloudInfoResult = { + isAws: boolean; + isGcp: boolean; + isAzure: boolean; +}; diff --git a/src/views/webviewController.ts b/src/views/webviewController.ts index 8637b06e5..dd9ec2be4 100644 --- a/src/views/webviewController.ts +++ b/src/views/webviewController.ts @@ -4,7 +4,7 @@ import path from 'path'; import ConnectionController, { ConnectionTypes } from '../connectionController'; -import TelemetryController from '../telemetry/telemetryController'; +import TelemetryService from '../telemetry/telemetryService'; import { MESSAGE_FROM_WEBVIEW_TO_EXTENSION, MESSAGE_TYPES, @@ -60,14 +60,14 @@ export const getWebviewContent = ( export default class WebviewController { _connectionController: ConnectionController; - _telemetryController: TelemetryController; + _telemetryService: TelemetryService; constructor( connectionController: ConnectionController, - telemetryController: TelemetryController + telemetryService: TelemetryService ) { this._connectionController = connectionController; - this._telemetryController = telemetryController; + this._telemetryService = telemetryService; } handleWebviewConnectAttempt = async ( @@ -173,7 +173,7 @@ export default class WebviewController { } return; case MESSAGE_TYPES.EXTENSION_LINK_CLICKED: - this._telemetryController.trackLinkClicked( + this._telemetryService.trackLinkClicked( message.screen, message.linkId ); From 69f25f426179d7669e7ae85e6bb844360c6e304e Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Wed, 13 Jan 2021 23:45:30 +0100 Subject: [PATCH 2/6] refactor: puch instance info type --- src/instanceInfoResultType.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/instanceInfoResultType.ts diff --git a/src/instanceInfoResultType.ts b/src/instanceInfoResultType.ts new file mode 100644 index 000000000..1c6317555 --- /dev/null +++ b/src/instanceInfoResultType.ts @@ -0,0 +1,25 @@ +/* eslint-disable camelcase */ +export type InstanceInfoResult = { + client: { + s: { + url: string; + } + }, + build: { + enterprise_module: boolean; + version?: string; + raw: { + buildEnvironment: { + target_arch: string | null; + target_os: string | null; + } + } + }, + dataLake: { + isDataLake: boolean; + }, + genuineMongoDB: { + isGenuine: boolean; + dbType: string; + } +}; From 6efc14b582b64c8bd9b02205f3e2c85653f480b9 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 14 Jan 2021 00:10:36 +0100 Subject: [PATCH 3/6] refactor: clean up some names and types --- src/editors/editDocumentCodeLensProvider.ts | 26 ++++++++++-------- src/editors/mongoDBDocumentService.ts | 8 ++++-- src/mdbExtensionController.ts | 9 +++++-- src/telemetry/telemetryService.ts | 27 +++++++++---------- src/test/suite/mdbExtensionController.test.ts | 6 ++--- 5 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/editors/editDocumentCodeLensProvider.ts b/src/editors/editDocumentCodeLensProvider.ts index 790cd1e4c..8afbbe69e 100644 --- a/src/editors/editDocumentCodeLensProvider.ts +++ b/src/editors/editDocumentCodeLensProvider.ts @@ -33,13 +33,14 @@ implements vscode.CodeLensProvider { } const { content, namespace, type } = playgroundResult; + const data = { content, namespace, source }; // Show code lenses only for the list of documents or a single document // that are returned by the find() method. if (type === 'Cursor') { - this._updateCodeLensesForCursor({ content, namespace, source }); + this._updateCodeLensesForCursor(data); } else if (type === 'Document') { - this._updateCodeLensesForDocument({ content, namespace, source }); + this._updateCodeLensesForDocument(data); } } @@ -52,22 +53,24 @@ implements vscode.CodeLensProvider { if (Array.isArray(data.content)) { const connectionId = this._connectionController.getActiveConnectionId(); + const { content, namespace, source } = data; // When the playground result is the collection, // show the first code lense after [{. let line = 2; - data.content.forEach((item) => { + content.forEach((item) => { // We need _id and namespace for code lenses // to be able to save the editable document. - if (item !== null && item._id && data.namespace) { + if (item !== null && item._id && namespace) { codeLensesInfo.push({ - source: data.source, - line, documentId: item._id, - namespace: data.namespace, + source, + line, + namespace, connectionId }); + // To calculate the position of the next open curly bracket, // we stringify the object and use a regular expression // so we can count the number of lines. @@ -86,17 +89,18 @@ implements vscode.CodeLensProvider { source: string }): void { const codeLensesInfo: ResultCodeLensInfo[] = []; + const { content, namespace, source } = data; - if (data.content._id && data.namespace) { + if (content._id && namespace) { const connectionId = this._connectionController.getActiveConnectionId(); // When the playground result is the single document, // show the single code lense after {. codeLensesInfo.push({ - source: data.source, + documentId: content._id, + source, line: 1, - documentId: data.content._id, - namespace: data.namespace, + namespace, connectionId }); } diff --git a/src/editors/mongoDBDocumentService.ts b/src/editors/mongoDBDocumentService.ts index a406fcb25..11361905a 100644 --- a/src/editors/mongoDBDocumentService.ts +++ b/src/editors/mongoDBDocumentService.ts @@ -101,9 +101,11 @@ export default class MongoDBDocumentService { this._statusView.hideMessage(); this._telemetryService.trackDocumentUpdated(source, true); } catch (error) { + const printableError = error as { message: string }; + this._statusView.hideMessage(); - return this._saveDocumentFailed(error.message); + return this._saveDocumentFailed(printableError.message); } } @@ -157,9 +159,11 @@ export default class MongoDBDocumentService { EJSON.stringify(documents[0]) ) as EJSON.SerializableTypes; } catch (error) { + const printableError = error as { message: string }; + this._statusView.hideMessage(); - return this._fetchDocumentFailed(error.message); + return this._fetchDocumentFailed(printableError.message); } } } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 25dceb0e7..2233cfdb7 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -15,7 +15,10 @@ import { } from './explorer'; import EXTENSION_COMMANDS from './commands'; import { LanguageServerController } from './language'; -import TelemetryService, { DOCUMENT_SOURCE_PLAYGROUND, DOCUMENT_SOURCE_TREEVIEW } from './telemetry/telemetryService'; +import TelemetryService, { + DOCUMENT_SOURCE_PLAYGROUND, + DOCUMENT_SOURCE_TREEVIEW +} from './telemetry/telemetryService'; import { StatusView } from './views'; import { createLogger } from './logging'; import { StorageController, StorageVariables } from './storage'; @@ -180,7 +183,9 @@ export default class MDBExtensionController implements vscode.Disposable { this.registerCommand( EXTENSION_COMMANDS.MDB_OPEN_MONGODB_DOCUMENT_FROM_PLAYGROUND, (data: ResultCodeLensInfo) => { - this._telemetryService.trackOpenMongoDBDocumentFromPlayground(DOCUMENT_SOURCE_PLAYGROUND); + this._telemetryService.trackOpenMongoDBDocumentFromPlayground( + DOCUMENT_SOURCE_PLAYGROUND + ); return this._editorsController.openMongoDBDocument(data); } diff --git a/src/telemetry/telemetryService.ts b/src/telemetry/telemetryService.ts index 37661b69d..6212164b3 100644 --- a/src/telemetry/telemetryService.ts +++ b/src/telemetry/telemetryService.ts @@ -267,34 +267,33 @@ export default class TelemetryService { dataService: DataServiceType, connectionType: ConnectionTypes ): void { - dataService.instance({}, async (error: any, data: unknown) => { + dataService.instance({}, async (error: any, data: InstanceInfoResult) => { if (error) { log.error('TELEMETRY data service error', error); } - const instanceInfo = data as InstanceInfoResult; - const dataServiceClient = dataService.client as { model: ConnectionModelType}; + const dataServiceClient = dataService.client as { model: ConnectionModelType }; - if (instanceInfo) { + if (data) { const firstServerHostname = dataServiceClient.model.hosts[0].host; const cloudInfo = await this.getCloudInfoFromDataService( firstServerHostname ); - const nonGenuineServerName = instanceInfo.genuineMongoDB.isGenuine + const nonGenuineServerName = data.genuineMongoDB.isGenuine ? null - : instanceInfo.genuineMongoDB.dbType; + : data.genuineMongoDB.dbType; const preparedProperties = { - is_atlas: !!ATLAS_REGEX.exec(instanceInfo.client.s.url), - is_localhost: !!LOCALHOST_REGEX.exec(instanceInfo.client.s.url), - is_data_lake: instanceInfo.dataLake.isDataLake, - is_enterprise: instanceInfo.build.enterprise_module, + is_atlas: !!ATLAS_REGEX.exec(data.client.s.url), + is_localhost: !!LOCALHOST_REGEX.exec(data.client.s.url), + is_data_lake: data.dataLake.isDataLake, + is_enterprise: data.build.enterprise_module, is_public_cloud: cloudInfo.isPublicCloud, public_cloud_name: cloudInfo.publicCloudName, - is_genuine: instanceInfo.genuineMongoDB.isGenuine, + is_genuine: data.genuineMongoDB.isGenuine, non_genuine_server_name: nonGenuineServerName, - server_version: instanceInfo.build.version, - server_arch: instanceInfo.build.raw.buildEnvironment.target_arch, - server_os: instanceInfo.build.raw.buildEnvironment.target_os, + server_version: data.build.version, + server_arch: data.build.raw.buildEnvironment.target_arch, + server_os: data.build.raw.buildEnvironment.target_os, is_used_connect_screen: connectionType === ConnectionTypes.CONNECTION_FORM, is_used_command_palette: diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index 843aac7dd..a815e2ebf 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -1335,7 +1335,7 @@ suite('MDBExtensionController Test Suite', function () { ); }); - test('document opened from a tree has treeview sorce', async () => { + test('document opened from a tree has treeview source', async () => { const mockDocument = { _id: 'pancakes', name: '', @@ -1360,7 +1360,7 @@ suite('MDBExtensionController Test Suite', function () { assert(mockFetchDocument.firstArg.source === 'treeview'); }); - test('document opened from playground results has treeview sorce', async () => { + test('document opened from playground results has treeview source', async () => { const documentItem = { source: 'playground', line: 1, @@ -1384,7 +1384,7 @@ suite('MDBExtensionController Test Suite', function () { assert(mockFetchDocument.firstArg.source === 'playground'); }); - test('fetchDocument recieves treeview source if document opened from tree', async () => { + test('fetchDocument recieves treeview source if document opened from a tree', async () => { const mockDocument = { _id: 'pancakes', name: '', From 8d187303eeb36ff25fedc8bb9832a3a5f0616fd9 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 14 Jan 2021 01:10:10 +0100 Subject: [PATCH 4/6] chore: update type dependencies --- package-lock.json | 223 ++++++++++++++++++++-------------- package.json | 48 ++++---- src/explorer/treeItemUtils.ts | 2 +- 3 files changed, 158 insertions(+), 115 deletions(-) diff --git a/package-lock.json b/package-lock.json index e23b36b23..e0786ee45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1480,6 +1480,18 @@ "typescript": "^3.8.3" }, "dependencies": { + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "typescript": { "version": "3.9.7", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", @@ -1694,20 +1706,10 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, "@sinonjs/samsam": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz", - "integrity": "sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -1882,9 +1884,9 @@ } }, "@types/jest": { - "version": "26.0.16", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.16.tgz", - "integrity": "sha512-Gp12+7tmKCgv9JjtltxUXokohCAEZfpJaEW5tn871SGRp8I+bRWBonQO7vW5NHwnAHe5dd50+Q4zyKuN35i09g==", + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", "dev": true, "requires": { "jest-diff": "^26.0.0", @@ -1914,9 +1916,9 @@ } }, "@types/yargs": { - "version": "15.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.11.tgz", - "integrity": "sha512-jfcNBxHFYJ4nPIacsi3woz1+kvUO6s1CyeEhtnDHBjHUMNj5UlW2GynmnSgiJJEdNg9yW5C8lfoNRZrHGv5EqA==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", + "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2003,15 +2005,6 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, - "@types/keytar": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@types/keytar/-/keytar-4.4.2.tgz", - "integrity": "sha512-xtQcDj9ruGnMwvSu1E2BH4SFa5Dv2PvSPd0CKEBLN5hEj/v5YpXJY+B6hAfuKIbvEomD7vJTc/P1s1xPNh2kRw==", - "dev": true, - "requires": { - "keytar": "*" - } - }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2025,15 +2018,15 @@ "dev": true }, "@types/mocha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", - "integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/node": { - "version": "14.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", - "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==", + "version": "14.14.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", + "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", "dev": true }, "@types/normalize-package-data": { @@ -2060,9 +2053,9 @@ "dev": true }, "@types/react": { - "version": "16.14.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.2.tgz", - "integrity": "sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", "dev": true, "requires": { "@types/prop-types": "*", @@ -2070,35 +2063,26 @@ }, "dependencies": { "csstype": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", "dev": true } } }, "@types/react-dom": { - "version": "16.9.10", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.10.tgz", - "integrity": "sha512-ItatOrnXDMAYpv6G8UCk2VhbYVTjZT9aorLtA/OzDN9XJ2GKcfam68jutoAcILdRjsRUO8qb7AmyObF77Q8QFw==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", "dev": true, "requires": { - "@types/react": "^16" - } - }, - "@types/redux": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz", - "integrity": "sha1-8evh5UEVGAcuT9/KXHbhbnTBOZo=", - "dev": true, - "requires": { - "redux": "*" + "@types/react": "*" } }, "@types/sinon": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.9.tgz", - "integrity": "sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.10.tgz", + "integrity": "sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA==", "requires": { "@types/sinonjs__fake-timers": "*" } @@ -2130,9 +2114,9 @@ "dev": true }, "@types/vscode": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.51.0.tgz", - "integrity": "sha512-C/jZ35OT5k/rsJyAK8mS1kM++vMcm89oSWegkzxRCvHllIq0cToZAkIDs6eCY4SKrvik3nrhELizyLcM0onbQA==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.52.0.tgz", + "integrity": "sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==", "dev": true }, "@types/ws": { @@ -4628,6 +4612,12 @@ "sha.js": "^2.4.8" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -13825,9 +13815,9 @@ } }, "mocha-junit-reporter": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz", - "integrity": "sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.0.0.tgz", + "integrity": "sha512-20HoWh2HEfhqmigfXOKUhZQyX23JImskc37ZOhIjBKoBEsb+4cAFRJpAVhFpnvsztLklW/gFVzsrobjLwmX4lA==", "dev": true, "requires": { "debug": "^2.2.0", @@ -13944,9 +13934,9 @@ } }, "mongodb-ace-autocompleter": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/mongodb-ace-autocompleter/-/mongodb-ace-autocompleter-0.4.13.tgz", - "integrity": "sha512-1F9tgb3/6D1kpwR6yBJP4FjWSjtsqUlNEKdAQk035kjhrF6vnSXgZzDta3917caXqvR7YvHYJ9LD1fQAq/MYXw==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/mongodb-ace-autocompleter/-/mongodb-ace-autocompleter-0.4.14.tgz", + "integrity": "sha512-j49THhGVBCwTR0IP/98SrS4DftEi3bsFnQPJ6g/d3l0P6uaOLo4B4aIueVZz4gGFr/Mv1o2tJDLiBVdgCAbjVQ==", "requires": { "semver": "^7.1.1" } @@ -17957,15 +17947,14 @@ } }, "sinon": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.1.tgz", - "integrity": "sha512-naPfsamB5KEE1aiioaoqJ6MEhdUs/2vtI5w1hPAXX/UwvoPjXcwh1m5HiKx0HGgKR8lQSoFIgY5jM6KK8VrS9w==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.3.tgz", + "integrity": "sha512-m+DyAWvqVHZtjnjX/nuShasykFeiZ+nPuEfD4G3gpvKGkXRhkF/6NSt2qN2FjZhfrcHXFzUzI+NLnk+42fnLEw==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.1", "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.2.0", + "@sinonjs/samsam": "^5.3.0", "diff": "^4.0.2", "nise": "^4.0.4", "supports-color": "^7.1.0" @@ -19225,23 +19214,77 @@ } }, "ts-loader": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.2.tgz", - "integrity": "sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==", + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.14.tgz", + "integrity": "sha512-Jt/hHlUnApOZjnSjTmZ+AbD5BGlQFx3f1D0nYuNKwz0JJnuDGHJas6az+FlWKwwRTu+26GXpv249A8UAnYUpqA==", "dev": true, "requires": { - "chalk": "^2.3.0", + "chalk": "^4.1.0", "enhanced-resolve": "^4.0.0", - "loader-utils": "^1.0.2", + "loader-utils": "^2.0.0", "micromatch": "^4.0.0", - "semver": "^6.0.0" + "semver": "^7.3.4" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, @@ -19251,11 +19294,13 @@ "integrity": "sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w==" }, "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, "requires": { "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.17", @@ -19631,9 +19676,9 @@ "dev": true }, "vsce": { - "version": "1.81.1", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.81.1.tgz", - "integrity": "sha512-1yWAYRxTx/PKSFZnuELe7GPyIo70H/XKJqf6wGikofUK3f3TCNGI6F9xkTQFvXKNe0AygUuxN7kITyPIQGMP+w==", + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.83.0.tgz", + "integrity": "sha512-gyF/xtCOFcKO+EvC0FQu5jPECHz2XKMWcw62gqwJJ22lVvlj58t49sWe1IGl9S5NpxCek+QMm6V9i/cDwGWs/Q==", "dev": true, "requires": { "azure-devops-node-api": "^7.2.0", @@ -19659,9 +19704,9 @@ }, "dependencies": { "commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true }, "leven": { diff --git a/package.json b/package.json index 2e74e3d6c..e2aec9c8d 100644 --- a/package.json +++ b/package.json @@ -868,25 +868,23 @@ }, "devDependencies": { "@types/analytics-node": "^3.1.4", - "@types/chai": "^4.2.9", + "@types/chai": "^4.2.14", "@types/chai-fs": "^2.0.2", - "@types/chai-json-schema": "^1.4.5", - "@types/classnames": "^2.2.10", + "@types/chai-json-schema": "^1.4.6", + "@types/classnames": "^2.2.11", "@types/debug": "^4.1.5", - "@types/enzyme": "^3.10.7", - "@types/glob": "^7.1.1", - "@types/jest": "^26.0.14", - "@types/keytar": "4.4.2", - "@types/mocha": "^8.0.3", - "@types/node": "^14.11.5", - "@types/react": "^16.9.25", - "@types/react-dom": "^16.9.5", - "@types/redux": "^3.6.0", - "@types/sinon": "^9.0.1", - "@types/vscode": "^1.49.0", - "@types/ws": "^7.2.4", - "@typescript-eslint/eslint-plugin": "^4.12.0", - "@typescript-eslint/parser": "^4.12.0", + "@types/enzyme": "^3.10.8", + "@types/glob": "^7.1.3", + "@types/jest": "^26.0.20", + "@types/mocha": "^8.2.0", + "@types/node": "^14.14.20", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@types/sinon": "^9.0.10", + "@types/vscode": "^1.52.0", + "@types/ws": "^7.4.0", + "@typescript-eslint/eslint-plugin": "^4.13.0", + "@typescript-eslint/parser": "^4.13.0", "autoprefixer": "^9.7.5", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", @@ -910,24 +908,24 @@ "meow": "^6.0.1", "mkdirp": "^1.0.4", "mocha": "^8.2.1", - "mocha-junit-reporter": "^1.23.3", + "mocha-junit-reporter": "^2.0.0", "mocha-multi": "^1.1.3", - "mongodb-ace-autocompleter": "^0.4.13", + "mongodb-ace-autocompleter": "^0.4.14", "mongodb-runner": "^4.8.0", "node-loader": "^0.6.0", "npm-run-all": "^4.1.5", "ora": "^4.0.3", "postcss-loader": "^3.0.0", "pre-commit": "^1.2.2", - "sinon": "^9.0.0", + "sinon": "^9.2.3", "sinon-chai": "^3.5.0", "style-loader": "^1.1.3", - "ts-jest": "^26.4.1", - "ts-loader": "^6.2.2", - "ts-node": "^8.6.2", + "ts-jest": "^26.4.4", + "ts-loader": "^8.0.14", + "ts-node": "^9.1.1", "typescript": "^4.1.3", - "vsce": "^1.81.1", - "vscode-test": "^1.3.0", + "vsce": "^1.83.0", + "vscode-test": "^1.4.1", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", "xvfb-maybe": "^0.2.1" diff --git a/src/explorer/treeItemUtils.ts b/src/explorer/treeItemUtils.ts index 65ac194ad..e6a1866d3 100644 --- a/src/explorer/treeItemUtils.ts +++ b/src/explorer/treeItemUtils.ts @@ -5,6 +5,6 @@ export function sortTreeItemsByLabel(treeItems: vscode.TreeItem[]): vscode.TreeI ( a: vscode.TreeItem, b: vscode.TreeItem - ) => (a.label || '').localeCompare(b.label || '') + ) => (a.label?.toString() || '').localeCompare(b.label?.toString() || '') ); } From a2827fde16b708a571256889e3082a331b5848ff Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 14 Jan 2021 12:17:34 +0100 Subject: [PATCH 5/6] chore: rollback to @types/vscode@^1.49.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2aec9c8d..b8e3216a1 100644 --- a/package.json +++ b/package.json @@ -881,7 +881,7 @@ "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/sinon": "^9.0.10", - "@types/vscode": "^1.52.0", + "@types/vscode": "^1.49.0", "@types/ws": "^7.4.0", "@typescript-eslint/eslint-plugin": "^4.13.0", "@typescript-eslint/parser": "^4.13.0", From efca24abb814bc11f57cbd2347ca27e20c4ebfc0 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 14 Jan 2021 15:15:33 +0100 Subject: [PATCH 6/6] refactor: promisify data service in telemetry --- src/editors/editDocumentCodeLensProvider.ts | 4 +-- src/editors/mongoDBDocumentService.ts | 6 +++-- src/mdbExtensionController.ts | 9 +++---- src/telemetry/telemetryService.ts | 27 ++++++++++++--------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/editors/editDocumentCodeLensProvider.ts b/src/editors/editDocumentCodeLensProvider.ts index 8afbbe69e..d3188848d 100644 --- a/src/editors/editDocumentCodeLensProvider.ts +++ b/src/editors/editDocumentCodeLensProvider.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import EXTENSION_COMMANDS from '../commands'; import type { OutputItem, ResultCodeLensInfo } from '../utils/types'; import ConnectionController from '../connectionController'; -import { DOCUMENT_SOURCE_PLAYGROUND } from '../telemetry/telemetryService'; +import { DocumentSource } from '../telemetry/telemetryService'; export default class EditDocumentCodeLensProvider implements vscode.CodeLensProvider { @@ -24,7 +24,7 @@ implements vscode.CodeLensProvider { } updateCodeLensesForPlayground(playgroundResult: OutputItem) { - const source = DOCUMENT_SOURCE_PLAYGROUND; + const source = DocumentSource.DOCUMENT_SOURCE_PLAYGROUND; if (!playgroundResult || !playgroundResult.content) { this._codeLensesInfo = []; diff --git a/src/editors/mongoDBDocumentService.ts b/src/editors/mongoDBDocumentService.ts index 11361905a..6375a6183 100644 --- a/src/editors/mongoDBDocumentService.ts +++ b/src/editors/mongoDBDocumentService.ts @@ -3,7 +3,7 @@ import { EJSON } from 'bson'; import DocumentIdStore from './documentIdStore'; import ConnectionController from '../connectionController'; import { StatusView } from '../views'; -import TelemetryService, { DOCUMENT_SOURCE_TREEVIEW } from '../telemetry/telemetryService'; +import TelemetryService, { DocumentSource } from '../telemetry/telemetryService'; import { createLogger } from '../logging'; import util from 'util'; import type { ResultCodeLensInfo } from '../utils/types'; @@ -46,7 +46,9 @@ export default class MongoDBDocumentService { _saveDocumentFailed(message: string): void { const errorMessage = `Unable to save document: ${message}`; - this._telemetryService.trackDocumentUpdated(DOCUMENT_SOURCE_TREEVIEW, false); + this._telemetryService.trackDocumentUpdated( + DocumentSource.DOCUMENT_SOURCE_TREEVIEW, false + ); throw new Error(errorMessage); } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 2233cfdb7..eb3aefa59 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -15,10 +15,7 @@ import { } from './explorer'; import EXTENSION_COMMANDS from './commands'; import { LanguageServerController } from './language'; -import TelemetryService, { - DOCUMENT_SOURCE_PLAYGROUND, - DOCUMENT_SOURCE_TREEVIEW -} from './telemetry/telemetryService'; +import TelemetryService, { DocumentSource } from './telemetry/telemetryService'; import { StatusView } from './views'; import { createLogger } from './logging'; import { StorageController, StorageVariables } from './storage'; @@ -184,7 +181,7 @@ export default class MDBExtensionController implements vscode.Disposable { EXTENSION_COMMANDS.MDB_OPEN_MONGODB_DOCUMENT_FROM_PLAYGROUND, (data: ResultCodeLensInfo) => { this._telemetryService.trackOpenMongoDBDocumentFromPlayground( - DOCUMENT_SOURCE_PLAYGROUND + DocumentSource.DOCUMENT_SOURCE_PLAYGROUND ); return this._editorsController.openMongoDBDocument(data); @@ -462,7 +459,7 @@ export default class MDBExtensionController implements vscode.Disposable { EXTENSION_COMMANDS.MDB_OPEN_MONGODB_DOCUMENT_FROM_TREE, (element: DocumentTreeItem): Promise => { return this._editorsController.openMongoDBDocument({ - source: DOCUMENT_SOURCE_TREEVIEW, + source: DocumentSource.DOCUMENT_SOURCE_TREEVIEW, documentId: element.documentId, namespace: element.namespace, connectionId: this._connectionController.getActiveConnectionId(), diff --git a/src/telemetry/telemetryService.ts b/src/telemetry/telemetryService.ts index 6212164b3..7def10aea 100644 --- a/src/telemetry/telemetryService.ts +++ b/src/telemetry/telemetryService.ts @@ -11,10 +11,12 @@ import type { ExecuteAllResult, CloudInfoResult } from '../utils/types'; import type { InstanceInfoResult } from '../instanceInfoResultType'; import { ConnectionModelType } from '../connectionModelType'; import fs from 'fs'; +import * as util from 'util'; -export const DOCUMENT_SOURCE_TREEVIEW = 'treeview'; - -export const DOCUMENT_SOURCE_PLAYGROUND = 'playground'; +export enum DocumentSource { + DOCUMENT_SOURCE_TREEVIEW = 'treeview', + DOCUMENT_SOURCE_PLAYGROUND = 'playground' +} const log = createLogger('telemetry'); @@ -111,7 +113,7 @@ export default class TelemetryService { ) { this._context = context; this._shouldTrackTelemetry = shouldTrackTelemetry || false; - this._segmentUserID = storageController.getUserID() || ''; + this._segmentUserID = storageController.getUserID(); this._segmentKey = this._readSegmentKey(); vscode.workspace.onDidOpenTextDocument((document) => { @@ -263,15 +265,14 @@ export default class TelemetryService { } } - trackNewConnection( + async trackNewConnection( dataService: DataServiceType, connectionType: ConnectionTypes - ): void { - dataService.instance({}, async (error: any, data: InstanceInfoResult) => { - if (error) { - log.error('TELEMETRY data service error', error); - } + ): Promise { + const instance = util.promisify(dataService.instance.bind(dataService)); + try { + const data = await instance({}) as InstanceInfoResult; const dataServiceClient = dataService.client as { model: ConnectionModelType }; if (data) { @@ -304,7 +305,9 @@ export default class TelemetryService { this.track(TelemetryEventTypes.NEW_CONNECTION, preparedProperties); } - }); + } catch (error) { + log.error('TELEMETRY track new connection', error); + } } trackCommandRun(command: string): void { @@ -369,7 +372,7 @@ export default class TelemetryService { this.track(TelemetryEventTypes.DOCUMENT_UPDATED, { source, success }); } - trackOpenMongoDBDocumentFromPlayground(source: string): void { + trackOpenMongoDBDocumentFromPlayground(source: DocumentSource): void { this.track(TelemetryEventTypes.DOCUMENT_EDITED, { source }); } }