Skip to content

Commit

Permalink
vCore: unified telemetry event names, removed obsolete telemetry calls (
Browse files Browse the repository at this point in the history
#2409)

vCore:
- Resource Tracking: Introduced tracking for resource and workspace trees, with further updates and improvements.
- Command Tracking: Enhanced command tracking.
- Telemetry Cleanup: Removed outdated telemetry data.
Shared:
- Telemetry Unification: Consolidated telemetry for 'getChildren' events at the Cosmos DB account level.
  • Loading branch information
tnaum-ms authored Nov 13, 2024
1 parent 6b7f7d8 commit 77681e4
Show file tree
Hide file tree
Showing 18 changed files with 449 additions and 420 deletions.
50 changes: 16 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -589,52 +589,42 @@
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.hello",
"title": "Dev: Say Hello!"
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.webview",
"title": "Dev: Show WebView"
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.dropCollection",
"command": "command.mongoClusters.dropCollection",
"title": "Drop Collection..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.dropDatabase",
"command": "command.mongoClusters.dropDatabase",
"title": "Drop Database..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.createCollection",
"command": "command.mongoClusters.createCollection",
"title": "Create Collection..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.createDatabase",
"command": "command.mongoClusters.createDatabase",
"title": "Create Database..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.importDocuments",
"command": "command.mongoClusters.importDocuments",
"title": "Import Documents into Collection..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.exportDocuments",
"command": "command.mongoClusters.exportDocuments",
"title": "Export Documents from Collection..."
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.launchShell",
"command": "command.mongoClusters.launchShell",
"title": "Launch Shell"
},
{
"category": "MongoDB (vCore)",
"command": "mongoClusters.cmd.removeWorkspaceConnection",
"command": "command.mongoClusters.removeWorkspaceConnection",
"title": "Remove Connection..."
}
],
Expand Down Expand Up @@ -1135,38 +1125,38 @@
"group": "1@1"
},
{
"command": "mongoClusters.cmd.dropCollection",
"command": "command.mongoClusters.dropCollection",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem == mongoClusters.item.collection"
},
{
"command": "mongoClusters.cmd.dropDatabase",
"command": "command.mongoClusters.dropDatabase",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem == mongoClusters.item.database"
},
{
"command": "mongoClusters.cmd.removeWorkspaceConnection",
"command": "command.mongoClusters.removeWorkspaceConnection",
"when": "vscodeDatabases.mongoClustersSupportEnabled && view == azureWorkspace && viewItem == mongoClusters.item.mongoCluster"
},
{
"command": "mongoClusters.cmd.createCollection",
"command": "command.mongoClusters.createCollection",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem == mongoClusters.item.database"
},
{
"command": "mongoClusters.cmd.createDatabase",
"command": "command.mongoClusters.createDatabase",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem =~ /mongoClusters.item.mongoCluster/i",
"group": "1@1"
},
{
"command": "mongoClusters.cmd.importDocuments",
"command": "command.mongoClusters.importDocuments",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem == mongoClusters.item.collection",
"group": "1@1"
},
{
"command": "mongoClusters.cmd.exportDocuments",
"command": "command.mongoClusters.exportDocuments",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem == mongoClusters.item.collection",
"group": "1@2"
},
{
"command": "mongoClusters.cmd.launchShell",
"command": "command.mongoClusters.launchShell",
"when": "vscodeDatabases.mongoClustersSupportEnabled && viewItem =~ /mongoClusters.item.(mongoCluster|database|collection)/i",
"group": "2@1"
}
Expand Down Expand Up @@ -1213,14 +1203,6 @@
{
"command": "postgreSQL.executeQuery",
"when": "editorLangId == 'postgres'"
},
{
"command": "mongoClusters.cmd.hello",
"when": "vscodeDatabases.mongoClustersSupportEnabled"
},
{
"command": "mongoClusters.cmd.webview",
"when": "vscodeDatabases.mongoClustersSupportEnabled"
}
]
},
Expand Down
1 change: 1 addition & 0 deletions src/AzureDBExperiences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { nonNullProp } from './utils/nonNull';

export enum API {
MongoDB = 'MongoDB',
MongoClusters = 'MongoClusters',
Graph = 'Graph',
Table = 'Table',
Core = 'Core', // Now called NoSQL
Expand Down
71 changes: 49 additions & 22 deletions src/docdb/tree/DocDBAccountTreeItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import {
type Resource,
} from '@azure/cosmos';
import {
callWithTelemetryAndErrorHandling,
type AzExtParentTreeItem,
type AzExtTreeItem,
type IActionContext,
type ICreateChildImplContext,
} from '@microsoft/vscode-azext-utils';
import type * as vscode from 'vscode';
import { API } from '../../AzureDBExperiences';
import { type IDeleteWizardContext } from '../../commands/deleteDatabaseAccount/IDeleteWizardContext';
import { deleteCosmosDBAccount } from '../../commands/deleteDatabaseAccount/deleteCosmosDBAccount';
import { getThemeAgnosticIconPath, SERVERLESS_CAPABILITY_NAME } from '../../constants';
Expand Down Expand Up @@ -98,31 +101,55 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
}

public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzExtTreeItem[]> {
if (this.root.isEmulator) {
const unableToReachEmulatorMessage: string =
"Unable to reach emulator. Please ensure it is started and connected to the port specified by the 'cosmosDB.emulator.port' setting, then try again.";
return await rejectOnTimeout(
2000,
() => super.loadMoreChildrenImpl(clearCache),
unableToReachEmulatorMessage,
);
} else {
try {
return await super.loadMoreChildrenImpl(clearCache);
} catch (e) {
if (e instanceof Error && isRbacException(e) && !this.hasShownRbacNotification) {
this.hasShownRbacNotification = true;
const principalId = (await getSignedInPrincipalIdForAccountEndpoint(this.root.endpoint)) ?? '';
// chedck if the principal ID matches the one that is signed in, otherwise this might be a security problem, hence show the error message
if (e.message.includes(`[${principalId}]`) && (await ensureRbacPermission(this, principalId))) {
const result = await callWithTelemetryAndErrorHandling(
'getChildren',
async (context: IActionContext): Promise<AzExtTreeItem[]> => {
context.telemetry.properties.parentContext = this.contextValue;

// move this to a shared file, currently it's defined in DocDBAccountTreeItem so I can't reference it here
if (this.contextValue.includes('cosmosDBDocumentServer')) {
context.telemetry.properties.experience = API.Core;
// same issue as above
} else if (this.contextValue.includes('cosmosDBGraphAccount')) {
context.telemetry.properties.experience = API.Graph;
// same issue as above
} else if (this.contextValue.includes('cosmosDBTableAccount')) {
context.telemetry.properties.experience = API.Table;
}

if (this.root.isEmulator) {
const unableToReachEmulatorMessage: string =
"Unable to reach emulator. Please ensure it is started and connected to the port specified by the 'cosmosDB.emulator.port' setting, then try again.";
return await rejectOnTimeout(
2000,
() => super.loadMoreChildrenImpl(clearCache),
unableToReachEmulatorMessage,
);
} else {
try {
return await super.loadMoreChildrenImpl(clearCache);
} else {
void showRbacPermissionError(this.fullId, principalId);
} catch (e) {
if (e instanceof Error && isRbacException(e) && !this.hasShownRbacNotification) {
this.hasShownRbacNotification = true;
const principalId =
(await getSignedInPrincipalIdForAccountEndpoint(this.root.endpoint)) ?? '';
// chedck if the principal ID matches the one that is signed in, otherwise this might be a security problem, hence show the error message
if (
e.message.includes(`[${principalId}]`) &&
(await ensureRbacPermission(this, principalId))
) {
return await super.loadMoreChildrenImpl(clearCache);
} else {
void showRbacPermissionError(this.fullId, principalId);
}
}
throw e; // rethrowing tells the resources extension to show the exception message in the tree
}
}
throw e; // rethrowing tells the resources extension to show the exception message in the tree
}
}
},
);

return result ?? [];
}

public async deleteTreeItemImpl(context: IDeleteWizardContext): Promise<void> {
Expand Down
117 changes: 67 additions & 50 deletions src/mongo/tree/MongoAccountTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import { type DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import {
appendExtensionUserAgent,
AzExtParentTreeItem,
callWithTelemetryAndErrorHandling,
parseError,
type AzExtTreeItem,
type IActionContext,
type ICreateChildImplContext,
} from '@microsoft/vscode-azext-utils';
import { type MongoClient } from 'mongodb';
import type * as vscode from 'vscode';
import { API } from '../../AzureDBExperiences';
import { deleteCosmosDBAccount } from '../../commands/deleteDatabaseAccount/deleteCosmosDBAccount';
import { type IDeleteWizardContext } from '../../commands/deleteDatabaseAccount/IDeleteWizardContext';
import { getThemeAgnosticIconPath, Links, testDb } from '../../constants';
Expand Down Expand Up @@ -63,56 +66,70 @@ export class MongoAccountTreeItem extends AzExtParentTreeItem {
}

public async loadMoreChildrenImpl(_clearCache: boolean): Promise<AzExtTreeItem[]> {
let mongoClient: MongoClient | undefined;
try {
let databases: IDatabaseInfo[];

if (!this.connectionString) {
throw new Error('Missing connection string');
}

// Azure MongoDB accounts need to have the name passed in for private endpoints
mongoClient = await connectToMongoClient(
this.connectionString,
this.databaseAccount ? nonNullProp(this.databaseAccount, 'name') : appendExtensionUserAgent(),
);

const databaseInConnectionString = getDatabaseNameFromConnectionString(this.connectionString);
if (databaseInConnectionString && !this.root.isEmulator) {
// emulator violates the connection string format
// If the database is in the connection string, that's all we connect to (we might not even have permissions to list databases)
databases = [
{
name: databaseInConnectionString,
empty: false,
},
];
} else {
// https://mongodb.github.io/node-mongodb-native/3.1/api/index.html
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const result: { databases: IDatabaseInfo[] } = await mongoClient.db(testDb).admin().listDatabases();
databases = result.databases;
}
return databases
.filter(
(database: IDatabaseInfo) =>
!(database.name && database.name.toLowerCase() === 'admin' && database.empty),
) // Filter out the 'admin' database if it's empty
.map(
(database) => new MongoDatabaseTreeItem(this, nonNullProp(database, 'name'), this.connectionString),
);
} catch (error) {
const message = parseError(error).message;
if (this.root?.isEmulator && message.includes('ECONNREFUSED')) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
error.message = `Unable to reach emulator. See ${Links.LocalConnectionDebuggingTips} for debugging tips.\n${message}`;
}
throw error;
} finally {
if (mongoClient) {
void mongoClient.close();
}
}
const result = await callWithTelemetryAndErrorHandling(
'getChildren',
async (context: IActionContext): Promise<AzExtTreeItem[]> => {
context.telemetry.properties.experience = API.MongoDB;
context.telemetry.properties.parentContext = this.contextValue;

let mongoClient: MongoClient | undefined;
try {
let databases: IDatabaseInfo[];

if (!this.connectionString) {
throw new Error('Missing connection string');
}

// Azure MongoDB accounts need to have the name passed in for private endpoints
mongoClient = await connectToMongoClient(
this.connectionString,
this.databaseAccount ? nonNullProp(this.databaseAccount, 'name') : appendExtensionUserAgent(),
);

const databaseInConnectionString = getDatabaseNameFromConnectionString(this.connectionString);
if (databaseInConnectionString && !this.root.isEmulator) {
// emulator violates the connection string format
// If the database is in the connection string, that's all we connect to (we might not even have permissions to list databases)
databases = [
{
name: databaseInConnectionString,
empty: false,
},
];
} else {
// https://mongodb.github.io/node-mongodb-native/3.1/api/index.html
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const result: { databases: IDatabaseInfo[] } = await mongoClient
.db(testDb)
.admin()
.listDatabases();
databases = result.databases;
}
return databases
.filter(
(database: IDatabaseInfo) =>
!(database.name && database.name.toLowerCase() === 'admin' && database.empty),
) // Filter out the 'admin' database if it's empty
.map(
(database) =>
new MongoDatabaseTreeItem(this, nonNullProp(database, 'name'), this.connectionString),
);
} catch (error) {
const message = parseError(error).message;
if (this.root?.isEmulator && message.includes('ECONNREFUSED')) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
error.message = `Unable to reach emulator. See ${Links.LocalConnectionDebuggingTips} for debugging tips.\n${message}`;
}
throw error;
} finally {
if (mongoClient) {
void mongoClient.close();
}
}
},
);

return result ?? [];
}

public async createChildImpl(context: ICreateChildImplContext): Promise<MongoDatabaseTreeItem> {
Expand Down
Loading

0 comments on commit 77681e4

Please sign in to comment.