Skip to content

Commit 390d75b

Browse files
authored
chore(telemetry): telemetry improvements VSCODE-673 (#914)
1 parent c0617e9 commit 390d75b

13 files changed

+268
-226
lines changed

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@
13611361
"@types/chai": "^4.3.20",
13621362
"@types/debug": "^4.1.12",
13631363
"@types/glob": "^7.2.0",
1364+
"@types/lodash": "^4.17.14",
13641365
"@types/micromatch": "^4.0.9",
13651366
"@types/mkdirp": "^2.0.0",
13661367
"@types/mocha": "^8.2.3",

src/connectionController.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export default class ConnectionController {
331331
}
332332
}
333333

334-
public sendTelemetry(
334+
private sendTelemetry(
335335
newDataService: DataService,
336336
connectionType: ConnectionTypes
337337
): void {
@@ -436,7 +436,7 @@ export default class ConnectionController {
436436
});
437437
const browserAuthCommand = vscode.workspace
438438
.getConfiguration('mdb')
439-
.get('browserCommandForOIDCAuth');
439+
.get<string>('browserCommandForOIDCAuth');
440440
dataService = await connectionAttempt.connect({
441441
...connectionOptions,
442442
oidc: {

src/explorer/explorerController.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
1-
import * as vscode from 'vscode';
1+
import type * as vscode from 'vscode';
22

33
import type ConnectionController from '../connectionController';
44
import { DataServiceEventTypes } from '../connectionController';
55
import ExplorerTreeController from './explorerTreeController';
6+
import { createTrackedTreeView } from '../utils/treeViewHelper';
7+
import type { TelemetryService } from '../telemetry';
68

79
export default class ExplorerController {
8-
private _connectionController: ConnectionController;
910
private _treeController: ExplorerTreeController;
1011
private _treeView?: vscode.TreeView<vscode.TreeItem>;
1112

12-
constructor(connectionController: ConnectionController) {
13-
this._connectionController = connectionController;
14-
this._treeController = new ExplorerTreeController(connectionController);
13+
constructor(
14+
private _connectionController: ConnectionController,
15+
private _telemetryService: TelemetryService
16+
) {
17+
this._treeController = new ExplorerTreeController(
18+
this._connectionController,
19+
this._telemetryService
20+
);
1521
}
1622

17-
createTreeView = (): void => {
23+
private createTreeView = (): void => {
1824
// Remove the listener that called this function.
1925
this._connectionController.removeEventListener(
2026
DataServiceEventTypes.CONNECTIONS_DID_CHANGE,
2127
this.createTreeView
2228
);
2329

2430
if (!this._treeView) {
25-
this._treeView = vscode.window.createTreeView(
31+
this._treeView = createTrackedTreeView(
2632
'mongoDBConnectionExplorer',
27-
{
28-
treeDataProvider: this._treeController,
29-
}
33+
this._treeController,
34+
this._telemetryService
3035
);
3136
this._treeController.activateTreeViewEventHandlers(this._treeView);
3237
}
3338
};
3439

35-
activateConnectionsTreeView(): void {
40+
public activateConnectionsTreeView(): void {
3641
// Listen for a change in connections to occur before we create the tree
3742
// so that we show the `viewsWelcome` before any connections are added.
3843
this._connectionController.addEventListener(
@@ -41,7 +46,7 @@ export default class ExplorerController {
4146
);
4247
}
4348

44-
deactivate(): void {
49+
public deactivate(): void {
4550
if (this._treeController) {
4651
this._treeController.removeListeners();
4752
}
@@ -52,7 +57,7 @@ export default class ExplorerController {
5257
}
5358
}
5459

55-
refresh(): boolean {
60+
public refresh(): boolean {
5661
if (this._treeController) {
5762
return this._treeController.refresh();
5863
}

src/explorer/explorerTreeController.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,25 @@ import { DOCUMENT_LIST_ITEM, CollectionTypes } from './documentListTreeItem';
99
import EXTENSION_COMMANDS from '../commands';
1010
import { sortTreeItemsByLabel } from './treeItemUtils';
1111
import type { LoadedConnection } from '../storage/connectionStorage';
12+
import {
13+
TreeItemExpandedTelemetryEvent,
14+
type TelemetryService,
15+
} from '../telemetry';
16+
import type TreeItemParentInterface from './treeItemParentInterface';
1217

1318
const log = createLogger('explorer tree controller');
1419

1520
export default class ExplorerTreeController
1621
implements vscode.TreeDataProvider<vscode.TreeItem>
1722
{
18-
private _connectionController: ConnectionController;
1923
private _connectionTreeItems: { [key: string]: ConnectionTreeItem };
2024

21-
constructor(connectionController: ConnectionController) {
25+
constructor(
26+
private _connectionController: ConnectionController,
27+
private _telemetryService: TelemetryService
28+
) {
2229
this._onDidChangeTreeData = new vscode.EventEmitter<void>();
2330
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
24-
this._connectionController = connectionController;
2531

2632
// Subscribe to changes in the connections.
2733
this._connectionController.addEventListener(
@@ -46,14 +52,19 @@ export default class ExplorerTreeController
4652
activateTreeViewEventHandlers = (
4753
treeView: vscode.TreeView<vscode.TreeItem>
4854
): void => {
49-
treeView.onDidCollapseElement((event: any) => {
55+
treeView.onDidCollapseElement((event) => {
5056
log.info('Tree item was collapsed', event.element.label);
5157

52-
if (event.element.onDidCollapse) {
53-
event.element.onDidCollapse();
58+
const treeItem = event.element as vscode.TreeItem &
59+
TreeItemParentInterface;
60+
if (treeItem.onDidCollapse) {
61+
treeItem.onDidCollapse();
5462
}
5563

56-
if (event.element.doesNotRequireTreeUpdate) {
64+
if (
65+
'doesNotRequireTreeUpdate' in treeItem &&
66+
treeItem.doesNotRequireTreeUpdate
67+
) {
5768
// When the element is already loaded (synchronous), we do not need to
5869
// fully refresh the tree.
5970
return;
@@ -62,20 +73,29 @@ export default class ExplorerTreeController
6273
this._onTreeItemUpdate();
6374
});
6475

65-
treeView.onDidExpandElement(async (event: any): Promise<void> => {
66-
log.info('Connection tree item was expanded', {
67-
connectionId: event.element.connectionId,
68-
connectionName: event.element.label,
69-
isExpanded: event.element.isExpanded,
76+
treeView.onDidExpandElement(async (event): Promise<void> => {
77+
const treeItem = event.element as vscode.TreeItem &
78+
TreeItemParentInterface;
79+
this._telemetryService.track(
80+
new TreeItemExpandedTelemetryEvent(treeItem)
81+
);
82+
83+
log.info('Explorer tree item was expanded', {
84+
type: treeItem.contextValue,
85+
connectionName: treeItem.label,
86+
isExpanded: treeItem.isExpanded,
7087
});
7188

72-
if (!event.element.onDidExpand) {
89+
if (!treeItem.onDidExpand) {
7390
return;
7491
}
7592

76-
await event.element.onDidExpand();
93+
await treeItem.onDidExpand();
7794

78-
if (event.element.doesNotRequireTreeUpdate) {
95+
if (
96+
'doesNotRequireTreeUpdate' in treeItem &&
97+
treeItem.doesNotRequireTreeUpdate
98+
) {
7999
// When the element is already loaded (synchronous), we do not
80100
// need to fully refresh the tree.
81101
return;

src/explorer/helpExplorer.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
import * as vscode from 'vscode';
1+
import type * as vscode from 'vscode';
22
import HelpTree from './helpTree';
33
import type { TelemetryService } from '../telemetry';
4+
import { createTrackedTreeView } from '../utils/treeViewHelper';
45

56
export default class HelpExplorer {
67
_treeController: HelpTree;
78
_treeView?: vscode.TreeView<vscode.TreeItem>;
89

9-
constructor() {
10+
constructor(private _telemetryService: TelemetryService) {
1011
this._treeController = new HelpTree();
1112
}
1213

13-
activateHelpTreeView(telemetryService: TelemetryService): void {
14+
activateHelpTreeView(): void {
1415
if (!this._treeView) {
15-
this._treeView = vscode.window.createTreeView('mongoDBHelpExplorer', {
16-
treeDataProvider: this._treeController,
17-
});
16+
this._treeView = createTrackedTreeView(
17+
'mongoDBHelpExplorer',
18+
this._treeController,
19+
this._telemetryService
20+
);
1821
this._treeController.activateTreeViewEventHandlers(
1922
this._treeView,
20-
telemetryService
23+
this._telemetryService
2124
);
2225
}
2326
}

src/explorer/playgroundsExplorer.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import * as vscode from 'vscode';
1+
import type * as vscode from 'vscode';
22
import PlaygroundsTree from './playgroundsTree';
3+
import type { TelemetryService } from '../telemetry';
4+
import { createTrackedTreeView } from '../utils/treeViewHelper';
35

46
export default class PlaygroundsExplorer {
57
private _treeController: PlaygroundsTree;
68
private _treeView?: vscode.TreeView<vscode.TreeItem>;
79

8-
constructor() {
10+
constructor(private _telemetryService: TelemetryService) {
911
this._treeController = new PlaygroundsTree();
1012
}
1113

1214
private createPlaygroundsTreeView = (): void => {
1315
if (!this._treeView) {
14-
this._treeView = vscode.window.createTreeView(
16+
this._treeView = createTrackedTreeView(
1517
'mongoDBPlaygroundsExplorer',
16-
{
17-
treeDataProvider: this._treeController,
18-
}
18+
this._treeController,
19+
this._telemetryService
1920
);
2021
this._treeController.activateTreeViewEventHandlers(this._treeView);
2122
}

src/mdbExtensionController.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,11 @@ export default class MDBExtensionController implements vscode.Disposable {
103103
});
104104
this._languageServerController = new LanguageServerController(context);
105105
this._explorerController = new ExplorerController(
106-
this._connectionController
106+
this._connectionController,
107+
this._telemetryService
107108
);
108-
this._helpExplorer = new HelpExplorer();
109-
this._playgroundsExplorer = new PlaygroundsExplorer();
109+
this._helpExplorer = new HelpExplorer(this._telemetryService);
110+
this._playgroundsExplorer = new PlaygroundsExplorer(this._telemetryService);
110111
this._editDocumentCodeLensProvider = new EditDocumentCodeLensProvider(
111112
this._connectionController
112113
);
@@ -175,7 +176,7 @@ export default class MDBExtensionController implements vscode.Disposable {
175176

176177
async activate(): Promise<void> {
177178
this._explorerController.activateConnectionsTreeView();
178-
this._helpExplorer.activateHelpTreeView(this._telemetryService);
179+
this._helpExplorer.activateHelpTreeView();
179180
this._playgroundsExplorer.activatePlaygroundsTreeView();
180181
this._telemetryService.activateSegmentAnalytics();
181182
this._participantController.createParticipant(this._context);

src/telemetry/telemetryEvents.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,14 @@ abstract class TelemetryEventBase {
7676
export class PlaygroundExecutedTelemetryEvent implements TelemetryEventBase {
7777
type = 'Playground Code Executed';
7878
properties: {
79-
/** The type of the executed operation, e.g. 'insert', 'update', 'delete', 'query', 'aggregation', 'other' */
79+
/**
80+
* The type of the executed operation. Common CRUD operations are mapped to
81+
* 'insert', 'update', 'delete', 'query', 'aggregation'. Other operations return
82+
* the type of the result returned by the shell API - e.g. 'collection', 'database',
83+
* 'help', etc. for known shell types and 'string', 'number', 'undefined', etc. for
84+
* plain JS types. In the unlikely case the shell evaluator was unable to determine
85+
* a type, 'other' is returned.
86+
*/
8087
type: string | null;
8188

8289
/** Whether the entire script was run or just a part of it */
@@ -118,7 +125,7 @@ export class PlaygroundExecutedTelemetryEvent implements TelemetryEventBase {
118125
return 'query';
119126
}
120127

121-
return 'other';
128+
return shellApiType;
122129
}
123130
}
124131

@@ -627,6 +634,41 @@ export class PresetConnectionEditedTelemetryEvent
627634
}
628635
}
629636

637+
/** Reported when the extension side panel is opened. VSCode doesn't expose
638+
* a subscribable event for this, so we're inferring it by subscribing to
639+
* treeView.onDidChangeVisibility for all the extension treeviews and throttling
640+
* the events.
641+
*/
642+
export class SidePanelOpenedTelemetryEvent implements TelemetryEventBase {
643+
type = 'Side Panel Opened';
644+
properties: {};
645+
646+
constructor() {
647+
this.properties = {};
648+
}
649+
}
650+
651+
/**
652+
* Reported when a tree item from the collection explorer is expanded.
653+
*/
654+
export class TreeItemExpandedTelemetryEvent implements TelemetryEventBase {
655+
type = 'Section Expanded';
656+
properties: {
657+
/**
658+
* The name of the section - e.g. database, collection, etc. This is obtained from the
659+
* `contextValue` field of the tree item.
660+
* */
661+
section_name?: string;
662+
};
663+
664+
constructor(item: vscode.TreeItem) {
665+
// We suffix all tree item context values with 'TreeItem', which is redundant when sending to analytics.
666+
this.properties = {
667+
section_name: item.contextValue?.replace('TreeItem', ''),
668+
};
669+
}
670+
}
671+
630672
export type TelemetryEvent =
631673
| PlaygroundExecutedTelemetryEvent
632674
| LinkClickedTelemetryEvent
@@ -649,4 +691,6 @@ export type TelemetryEvent =
649691
| ParticipantChatOpenedFromActionTelemetryEvent
650692
| ParticipantInputBoxSubmittedTelemetryEvent
651693
| ParticipantResponseGeneratedTelemetryEvent
652-
| PresetConnectionEditedTelemetryEvent;
694+
| PresetConnectionEditedTelemetryEvent
695+
| SidePanelOpenedTelemetryEvent
696+
| TreeItemExpandedTelemetryEvent;

0 commit comments

Comments
 (0)