Added data points for Table Designer (#18182)

* added server infor and metrics for table designer

* update generate script

* pr comments

* format more files

* pr comments

* make changes to core

* remove unused imports

* add server info

* revert enum change and add publish event

* format doc

* nitpicks

* remove os version

* remove modifier from telemetry info

* remove error message
This commit is contained in:
Aditya Bist
2022-02-02 12:40:05 -08:00
committed by GitHub
parent de1a0f4f0f
commit de5090e47a
14 changed files with 117 additions and 19 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { DesignerViewModel, DesignerEdit, DesignerComponentInput, DesignerView, DesignerTab, DesignerDataPropertyInfo, DropDownProperties, DesignerTableProperties, DesignerEditProcessedEventArgs, DesignerAction, DesignerStateChangedEventArgs } from 'sql/workbench/browser/designer/interfaces';
import { DesignerViewModel, DesignerEdit, DesignerComponentInput, DesignerView, DesignerTab, DesignerDataPropertyInfo, DropDownProperties, DesignerTableProperties, DesignerEditProcessedEventArgs, DesignerAction, DesignerStateChangedEventArgs, DesignerEditPath } from 'sql/workbench/browser/designer/interfaces';
import { TableDesignerProvider } from 'sql/workbench/services/tableDesigner/common/interface';
import { localize } from 'vs/nls';
import { designers } from 'sql/workbench/api/common/sqlExtHostTypes';
@@ -14,6 +14,8 @@ import { deepClone, equals } from 'vs/base/common/objects';
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TableDesignerPublishDialogResult, TableDesignerPublishDialog } from 'sql/workbench/services/tableDesigner/browser/tableDesignerPublishDialog';
import { IAdsTelemetryService, ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
import { TelemetryAction, TelemetryView } from 'sql/platform/telemetry/common/telemetryKeys';
export class TableDesignerComponentInput implements DesignerComponentInput {
@@ -31,9 +33,15 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
public readonly onEditProcessed: Event<DesignerEditProcessedEventArgs> = this._onEditProcessed.event;
public readonly onStateChange: Event<DesignerStateChangedEventArgs> = this._onStateChange.event;
private readonly designerEditTypeDisplayValue: { [key: number]: string } = {
0: 'Add', 1: 'Remove', 2: 'Update'
};
constructor(private readonly _provider: TableDesignerProvider,
private _tableInfo: azdata.designers.TableInfo,
private _telemetryInfo: ITelemetryEventProperties,
@INotificationService private readonly _notificationService: INotificationService,
@IAdsTelemetryService readonly _adsTelemetryService: IAdsTelemetryService,
@IQueryEditorService private readonly _queryEditorService: IQueryEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService) {
}
@@ -63,6 +71,11 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
}
processEdit(edit: DesignerEdit): void {
const telemetryInfo = this.createTelemetryInfo();
telemetryInfo.tableObjectType = this.getObjectTypeFromPath(edit.path);
const editAction = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner,
this.designerEditTypeDisplayValue[edit.type]).withAdditionalProperties(telemetryInfo);
const startTime = new Date().getTime();
this.updateState(this.valid, this.dirty, 'processEdit');
this._provider.processTableEdit(this._tableInfo, edit).then(
result => {
@@ -76,10 +89,15 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
errors: result.errors
}
});
editAction.withAdditionalMeasurements({
'elapsedTimeMs': new Date().getTime() - startTime
}).send();
},
error => {
this._notificationService.error(localize('tableDesigner.errorProcessingEdit', "An error occured while processing the change: {0}", error?.message ?? error));
this.updateState(this.valid, this.dirty);
this._adsTelemetryService.createErrorEvent(TelemetryView.TableDesigner,
this.designerEditTypeDisplayValue[edit.type]).withAdditionalProperties(telemetryInfo).send();
}
);
}
@@ -90,35 +108,49 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
message: localize('tableDesigner.generatingScript', "Generating script..."),
sticky: true
});
const telemetryInfo = this.createTelemetryInfo();
const generateScriptEvent = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.GenerateScript).withAdditionalProperties(telemetryInfo);
const startTime = new Date().getTime();
try {
this.updateState(this.valid, this.dirty, 'generateScript');
const script = await this._provider.generateScript(this._tableInfo);
this._queryEditorService.newSqlEditor({ initalContent: script });
this.updateState(this.valid, this.dirty);
notificationHandle.updateMessage(localize('tableDesigner.generatingScriptCompleted', "Script generated."));
generateScriptEvent.withAdditionalMeasurements({
'elapsedTimeMs': new Date().getTime() - startTime
}).send();
} catch (error) {
notificationHandle.updateSeverity(Severity.Error);
notificationHandle.updateMessage(localize('tableDesigner.generateScriptError', "An error occured while generating the script: {0}", error?.message ?? error));
this.updateState(this.valid, this.dirty);
this._adsTelemetryService.createErrorEvent(TelemetryView.TableDesigner, TelemetryAction.GenerateScript).withAdditionalProperties(telemetryInfo).send();
}
}
async publishChanges(): Promise<void> {
const telemetryInfo = this.createTelemetryInfo();
const publishEvent = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.PublishChanges).withAdditionalProperties(telemetryInfo);
const saveNotificationHandle = this._notificationService.notify({
severity: Severity.Info,
message: localize('tableDesigner.savingChanges', "Saving table designer changes..."),
sticky: true
});
const startTime = new Date().getTime();
try {
this.updateState(this.valid, this.dirty, 'save');
await this._provider.publishChanges(this._tableInfo);
this._originalViewModel = this._viewModel;
this.updateState(true, false);
saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeSuccess', "The changes have been successfully published."));
publishEvent.withAdditionalMeasurements({
'elapsedTimeMs': new Date().getTime() - startTime
}).send();
} catch (error) {
saveNotificationHandle.updateSeverity(Severity.Error);
saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeError', "An error occured while publishing changes: {0}", error?.message ?? error));
this.updateState(this.valid, this.dirty);
this._adsTelemetryService.createErrorEvent(TelemetryView.TableDesigner, TelemetryAction.PublishChanges).withAdditionalProperties(telemetryInfo).send();
}
}
@@ -580,4 +612,36 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
this._viewModel[property] = {};
}
}
private createTelemetryInfo(): ITelemetryEventProperties {
let telemetryInfo = {
provider: this._provider.providerId,
isNewTable: this._tableInfo.isNewTable,
};
Object.assign(telemetryInfo, this._telemetryInfo);
return telemetryInfo;
}
/**
* 1. 'Add' scenario
a. ['propertyName1']. Example: add a column to the columns property: ['columns'].
b. ['propertyName1',index-1,'propertyName2']. Example: add a column mapping to the first foreign key: ['foreignKeys',0,'mappings'].
2. 'Update' scenario
a. ['propertyName1']. Example: update the name of the table: ['name'].
b. ['propertyName1',index-1,'propertyName2']. Example: update the name of a column: ['columns',0,'name'].
c. ['propertyName1',index-1,'propertyName2',index-2,'propertyName3']. Example: update the source column of an entry in a foreign key's column mapping table: ['foreignKeys',0,'mappings',0,'source'].
3. 'Remove' scenario
a. ['propertyName1',index-1]. Example: remove a column from the columns property: ['columns',0'].
b. ['propertyName1',index-1,'proper
The return values would be the propertyNames followed by slashes in level order. Eg.: propertyName1/propertyName2/...
*/
private getObjectTypeFromPath(path: DesignerEditPath): string {
let typeArray = [];
for (let i = 0; i < path.length; i++) {
if (i % 2 === 0) {
typeArray.push(path[i]);
}
}
return typeArray.join('/');
}
}