diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 144010c0eb..3a3cff1783 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1111,6 +1111,10 @@ declare module 'azdata' { * The input validation error. */ inputValidationError?: string; + /** + * Metadata related to the table + */ + metadata?: { [key: string]: string }; } /** @@ -1129,6 +1133,10 @@ declare module 'azdata' { * The new view. */ view: TableDesignerView; + /** + * Metadata related to the table to be captured + */ + metadata?: { [key: string]: string }; } export interface GeneratePreviewReportResult { @@ -1144,6 +1152,10 @@ declare module 'azdata' { * The table schema validation error. */ schemaValidationError?: string; + /** + * Metadata related to the table to be captured + */ + metadata?: { [key: string]: string }; } } diff --git a/src/sql/platform/telemetry/common/telemetryKeys.ts b/src/sql/platform/telemetry/common/telemetryKeys.ts index a71900369a..0b77fbc0cb 100644 --- a/src/sql/platform/telemetry/common/telemetryKeys.ts +++ b/src/sql/platform/telemetry/common/telemetryKeys.ts @@ -71,6 +71,7 @@ export const enum TelemetryAction { Click = 'Click', FirewallRuleRequested = 'FirewallRuleCreated', GenerateScript = 'GenerateScript', + GeneratePreviewReport = 'GeneratePreviewReport', GetDataGridItems = 'GetDataGridItems', GetDataGridColumns = 'GetDataGridColumns', ModelViewDashboardOpened = 'ModelViewDashboardOpened', diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts index 0524361b43..9c19b29953 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts @@ -17,6 +17,7 @@ import { TableDesignerPublishDialogResult, TableDesignerPublishDialog } from 'sq import { IAdsTelemetryService, ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry'; import { TelemetryAction, TelemetryView } from 'sql/platform/telemetry/common/telemetryKeys'; import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; +import { TableDesignerMetadata } from 'sql/workbench/services/tableDesigner/browser/tableDesignerMetadata'; const ErrorDialogTitle: string = localize('tableDesigner.ErrorDialogTitle', "Table Designer Error"); export class TableDesignerComponentInput implements DesignerComponentInput { @@ -107,9 +108,10 @@ export class TableDesignerComponentInput implements DesignerComponentInput { refreshView: !!result.view } }); + const metadataTelemetryInfo = TableDesignerMetadata.getTelemetryInfo(this._provider.providerId, result.metadata); editAction.withAdditionalMeasurements({ 'elapsedTimeMs': new Date().getTime() - startTime - }).send(); + }).withAdditionalProperties(metadataTelemetryInfo).send(); }, error => { this._errorMessageService.showDialog(Severity.Error, ErrorDialogTitle, localize('tableDesigner.errorProcessingEdit', "An error occured while processing the change: {0}", error?.message ?? error)); @@ -164,9 +166,10 @@ export class TableDesignerComponentInput implements DesignerComponentInput { this.tableInfo = result.newTableInfo; this.updateState(true, false); this._onRefreshRequested.fire(); + const metadataTelemetryInfo = TableDesignerMetadata.getTelemetryInfo(this._provider.providerId, result.metadata); publishEvent.withAdditionalMeasurements({ 'elapsedTimeMs': new Date().getTime() - startTime - }).send(); + }).withAdditionalProperties(metadataTelemetryInfo).send(); } catch (error) { this._errorMessageService.showDialog(Severity.Error, ErrorDialogTitle, localize('tableDesigner.publishChangeError', "An error occured while publishing changes: {0}", error?.message ?? error)); this.updateState(this.valid, this.dirty); @@ -180,16 +183,23 @@ export class TableDesignerComponentInput implements DesignerComponentInput { message: localize('tableDesigner.generatingPreviewReport', "Generating preview report..."), sticky: true }); - + const telemetryInfo = this.createTelemetryInfo(); + const generatePreviewEvent = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.GeneratePreviewReport).withAdditionalProperties(telemetryInfo); + const startTime = new Date().getTime(); let previewReportResult: azdata.designers.GeneratePreviewReportResult; try { this.updateState(this.valid, this.dirty, 'generateReport'); previewReportResult = await this._provider.generatePreviewReport(this.tableInfo); + const metadataTelemetryInfo = TableDesignerMetadata.getTelemetryInfo(this._provider.providerId, previewReportResult.metadata); + generatePreviewEvent.withAdditionalMeasurements({ + 'elapsedTimeMs': new Date().getTime() - startTime + }).withAdditionalProperties(metadataTelemetryInfo).send(); reportNotificationHandle.close(); this.updateState(this.valid, this.dirty); } catch (error) { this._errorMessageService.showDialog(Severity.Error, ErrorDialogTitle, localize('tableDesigner.generatePreviewReportError', "An error occured while generating preview report: {0}", error?.message ?? error)); this.updateState(this.valid, this.dirty); + this._adsTelemetryService.createErrorEvent(TelemetryView.TableDesigner, TelemetryAction.GeneratePreviewReport).withAdditionalProperties(telemetryInfo).send(); return; } if (previewReportResult.schemaValidationError) { diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerMetadata.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerMetadata.ts new file mode 100644 index 0000000000..b251710480 --- /dev/null +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerMetadata.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export class TableDesignerMetadata { + + // Allowed metadata for every provider + public static mssqlAllowedMetdata: Set = new Set(['isNode', 'isEdge', 'isSystemVersioned']); + + // Provider ID to allowed metadata list set mapping + public static providerMetadataMap: Map> = new Map>([ + ['MSSQL', TableDesignerMetadata.mssqlAllowedMetdata] + ]); + + /** + * Validates given metadata and adds metadata from the allowed list + * @param providerId provider ID for the table designer provider + * @param metadata incoming metadata from the table designer provider + * @returns filtered telemetry info with only allowed metadata points + */ + public static getTelemetryInfo(providerId: string, metadata: { [key: string]: string }): { [key: string]: string } { + if (!TableDesignerMetadata.providerMetadataMap.has(providerId)) { + return undefined; + } + const allowedSet = TableDesignerMetadata.providerMetadataMap.get(providerId); + for (const key of Object.keys(metadata)) { + if (!allowedSet.has(key)) { + delete metadata[key]; + } + } + return metadata; + } + +} diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts index 2ff54f9517..212d1c2f91 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts @@ -44,10 +44,7 @@ export class TableDesignerService implements ITableDesignerService { } public async openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): Promise { - this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.Open).withAdditionalProperties({ - provider: providerId, - newTable: tableInfo.isNewTable - }).send(); + this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.Open).withAdditionalProperties(telemetryInfo).send(); const provider = this.getProvider(providerId); const tableDesignerInput = this._instantiationService.createInstance(TableDesignerInput, provider, tableInfo, telemetryInfo); await this._editorService.openEditor(tableDesignerInput, { pinned: true }, ACTIVE_GROUP);