mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
add telemetry to undo/redo at cell level (#17804)
* add telemetry to undo/redo at cell level *add sendTelemetryActionEvent that sends telemetry from notebook model
This commit is contained in:
@@ -96,7 +96,9 @@ export const enum NbTelemetryAction {
|
||||
RunAll = 'RunNotebook',
|
||||
AddCell = 'AddCell',
|
||||
KernelChanged = 'KernelChanged',
|
||||
NewNotebookFromConnections = 'NewNotebookWithConnectionProfile'
|
||||
NewNotebookFromConnections = 'NewNotebookWithConnectionProfile',
|
||||
UndoCell = 'UndoCell',
|
||||
RedoCell = 'RedoCell'
|
||||
}
|
||||
|
||||
export const enum TelemetryPropertyName {
|
||||
|
||||
@@ -61,7 +61,6 @@ export class AddCellAction extends Action {
|
||||
constructor(
|
||||
id: string, label: string, cssClass: string,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService,
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
@@ -76,16 +75,15 @@ export class AddCellAction extends Action {
|
||||
}
|
||||
if (context?.model) {
|
||||
context.model.addCell(this.cellType, index);
|
||||
context.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.AddCell, { cell_type: this.cellType });
|
||||
}
|
||||
} else {
|
||||
//Add Cell after current selected cell.
|
||||
const editor = this._notebookService.findNotebookEditor(context);
|
||||
const index = editor.cells?.findIndex(cell => cell.active) ?? 0;
|
||||
editor.addCell(this.cellType, index);
|
||||
editor.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.AddCell, { cell_type: this.cellType });
|
||||
}
|
||||
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.AddCell)
|
||||
.withAdditionalProperties({ cell_type: this.cellType })
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,19 +367,13 @@ export class RunAllCellsAction extends Action {
|
||||
id: string, label: string, cssClass: string,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService,
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
public override async run(context: URI): Promise<void> {
|
||||
try {
|
||||
const editor = this._notebookService.findNotebookEditor(context);
|
||||
|
||||
const azdata_notebook_guid: string = editor.model.getMetaValue('azdata_notebook_guid');
|
||||
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.RunAll)
|
||||
.withAdditionalProperties({ azdata_notebook_guid })
|
||||
.send();
|
||||
|
||||
editor.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RunAll);
|
||||
await editor.runAllCells();
|
||||
} catch (e) {
|
||||
this.notificationService.error(getErrorMessage(e));
|
||||
|
||||
@@ -29,6 +29,8 @@ import { MockQuickInputService } from 'sql/workbench/contrib/notebook/test/commo
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { Separator } from 'vs/base/common/actions';
|
||||
import { INotebookView, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
|
||||
|
||||
class TestClientSession extends ClientSessionStub {
|
||||
private _errorState: boolean = false;
|
||||
@@ -106,6 +108,9 @@ class TestNotebookModel extends NotebookModelStub {
|
||||
public override getStandardKernelFromName(name: string): IStandardKernelWithProvider {
|
||||
return this._standardKernelsMap.get(name);
|
||||
}
|
||||
|
||||
public override sendNotebookTelemetryActionEvent(action: TelemetryKeys.TelemetryAction | TelemetryKeys.NbTelemetryAction, additionalProperties?: ITelemetryEventProperties): void {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Notebook Actions', function (): void {
|
||||
@@ -113,9 +118,11 @@ suite('Notebook Actions', function (): void {
|
||||
let mockNotebookEditor: TypeMoq.Mock<INotebookEditor>;
|
||||
let mockNotebookService: TypeMoq.Mock<INotebookService>;
|
||||
const testUri = URI.parse('untitled');
|
||||
let testNotebookModel = new TestNotebookModel();
|
||||
|
||||
suiteSetup(function (): void {
|
||||
mockNotebookEditor = TypeMoq.Mock.ofType<INotebookEditor>(NotebookEditorStub);
|
||||
mockNotebookEditor.setup(x => x.model).returns(() => testNotebookModel);
|
||||
mockNotebookService = TypeMoq.Mock.ofType<INotebookService>(NotebookServiceStub);
|
||||
mockNotebookService.setup(x => x.findNotebookEditor(TypeMoq.It.isAny())).returns(uri => mockNotebookEditor.object);
|
||||
});
|
||||
@@ -129,7 +136,7 @@ suite('Notebook Actions', function (): void {
|
||||
let actualCellType: CellType;
|
||||
|
||||
|
||||
let action = new AddCellAction('TestId', 'TestLabel', 'TestClass', mockNotebookService.object, new NullAdsTelemetryService());
|
||||
let action = new AddCellAction('TestId', 'TestLabel', 'TestClass', mockNotebookService.object);
|
||||
action.cellType = testCellType;
|
||||
|
||||
// Normal use case
|
||||
@@ -196,7 +203,7 @@ suite('Notebook Actions', function (): void {
|
||||
let mockNotification = TypeMoq.Mock.ofType<INotificationService>(TestNotificationService);
|
||||
mockNotification.setup(n => n.notify(TypeMoq.It.isAny()));
|
||||
|
||||
let action = new RunAllCellsAction('TestId', 'TestLabel', 'TestClass', mockNotification.object, mockNotebookService.object, new NullAdsTelemetryService());
|
||||
let action = new RunAllCellsAction('TestId', 'TestLabel', 'TestClass', mockNotification.object, mockNotebookService.object);
|
||||
|
||||
// Normal use case
|
||||
mockNotebookEditor.setup(c => c.runAllCells()).returns(() => Promise.resolve(true));
|
||||
|
||||
@@ -21,6 +21,8 @@ import { IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { INotebookShowOptions } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
import { INotebookView, INotebookViewCell, INotebookViewMetadata, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
|
||||
|
||||
export class NotebookModelStub implements INotebookModel {
|
||||
constructor(private _languageInfo?: nb.ILanguageInfo, private _cells?: ICellModel[], private _testContents?: nb.INotebookContents) {
|
||||
@@ -158,6 +160,8 @@ export class NotebookModelStub implements INotebookModel {
|
||||
requestConnection(): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
sendNotebookTelemetryActionEvent(action: TelemetryKeys.TelemetryAction | TelemetryKeys.NbTelemetryAction, additionalProperties?: ITelemetryEventProperties): void {
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookFindModelStub implements INotebookFindModel {
|
||||
|
||||
@@ -29,7 +29,6 @@ import { tryMatchCellMagic, extractCellMagicCommandPlusArgs } from 'sql/workbenc
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IInsightOptions } from 'sql/workbench/common/editor/query/chartState';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
|
||||
@@ -94,7 +93,6 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
@optional(INotebookService) private _notebookService?: INotebookService,
|
||||
@optional(ICommandService) private _commandService?: ICommandService,
|
||||
@optional(IConfigurationService) private _configurationService?: IConfigurationService,
|
||||
@optional(IAdsTelemetryService) private _telemetryService?: IAdsTelemetryService,
|
||||
) {
|
||||
super();
|
||||
this.id = `${modelId++}`;
|
||||
@@ -568,10 +566,7 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
this._outputCounter = 0;
|
||||
// Hide IntelliSense suggestions list when running cell to match SSMS behavior
|
||||
this._commandService.executeCommand('hideSuggestWidget');
|
||||
const azdata_notebook_guid: string = this.notebookModel.getMetaValue('azdata_notebook_guid');
|
||||
this._telemetryService?.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.RunCell)
|
||||
.withAdditionalProperties({ cell_language: kernel.name, azdata_cell_guid: this._cellGuid, azdata_notebook_guid })
|
||||
.send();
|
||||
this.notebookModel.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RunCell, { cell_language: kernel.name, azdata_cell_guid: this._cellGuid });
|
||||
// If cell is currently running and user clicks the stop/cancel button, call kernel.interrupt()
|
||||
// This matches the same behavior as JupyterLab
|
||||
if (this.future && this.future.inProgress) {
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { ICellModel, MoveDirection } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export class MoveCellEdit implements IResourceUndoRedoElement {
|
||||
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
|
||||
label: string = localize('moveCellEdit', "Move Cell");
|
||||
resource = this.model.notebookUri;
|
||||
private readonly cellOperation = { cell_operation: 'move_cell' };
|
||||
|
||||
constructor(private model: NotebookModel, private cell: ICellModel, private moveDirection: MoveDirection) {
|
||||
}
|
||||
@@ -19,10 +21,12 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
|
||||
undo(): void {
|
||||
const direction = this.moveDirection === MoveDirection.Down ? MoveDirection.Up : MoveDirection.Down;
|
||||
this.model.moveCell(this.cell, direction, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.UndoCell, this.cellOperation);
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
this.model.moveCell(this.cell, this.moveDirection, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RedoCell, this.cellOperation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +34,14 @@ export class SplitCellEdit implements IResourceUndoRedoElement {
|
||||
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
|
||||
label: string = localize('splitCellEdit', "Split Cell");
|
||||
resource = this.model.notebookUri;
|
||||
private readonly cellOperation = { cell_operation: 'split_cell' };
|
||||
|
||||
constructor(private model: NotebookModel, private firstCell: ICellModel, private secondCell: ICellModel, private newLinesRemoved: string[]) {
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
this.model.mergeCells(this.firstCell, this.secondCell, this.newLinesRemoved);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.UndoCell, this.cellOperation);
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
@@ -47,16 +53,19 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
|
||||
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
|
||||
label: string = localize('deleteCellEdit', "Delete Cell");
|
||||
resource = this.model.notebookUri;
|
||||
private readonly cellOperation = { cell_operation: 'delete_cell' };
|
||||
|
||||
constructor(private model: NotebookModel, private cell: ICellModel, private index: number) {
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
this.model.insertCell(this.cell, this.index, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.UndoCell, this.cellOperation);
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
this.model.deleteCell(this.cell, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RedoCell, this.cellOperation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,15 +73,18 @@ export class AddCellEdit implements IResourceUndoRedoElement {
|
||||
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
|
||||
label: string = localize('addCellEdit', "Add Cell");
|
||||
resource = this.model.notebookUri;
|
||||
private readonly cellOperation = { cell_operation: 'add_cell' };
|
||||
|
||||
constructor(private model: NotebookModel, private cell: ICellModel, private index: number) {
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
this.model.deleteCell(this.cell, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.UndoCell, this.cellOperation);
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
this.model.insertCell(this.cell, this.index, false);
|
||||
this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RedoCell, this.cellOperation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import type { FutureInternal } from 'sql/workbench/services/notebook/browser/int
|
||||
import { ICellValue, ResultSetSummary } from 'sql/workbench/services/query/common/query';
|
||||
import { QueryResultId } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
|
||||
|
||||
|
||||
export enum ViewMode {
|
||||
Notebook,
|
||||
@@ -439,6 +442,12 @@ export interface INotebookModel {
|
||||
|
||||
requestConnection(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Create and send a Notebook Telemetry Event
|
||||
* @param action Telemetry action
|
||||
* @param additionalProperties Additional properties to send.
|
||||
*/
|
||||
sendNotebookTelemetryActionEvent(action: TelemetryKeys.TelemetryAction | TelemetryKeys.NbTelemetryAction, additionalProperties?: ITelemetryEventProperties): void;
|
||||
}
|
||||
|
||||
export interface NotebookContentChange {
|
||||
|
||||
@@ -25,7 +25,7 @@ import { uriPrefixes } from 'sql/platform/connection/common/utils';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
import { notebookConstants } from 'sql/workbench/services/notebook/browser/interfaces';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IAdsTelemetryService, ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
@@ -37,6 +37,8 @@ import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryText
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { AddCellEdit, DeleteCellEdit, MoveCellEdit, SplitCellEdit } from 'sql/workbench/services/notebook/browser/models/cellEdit';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
/*
|
||||
* Used to control whether a message in a dialog/wizard is displayed as an error,
|
||||
* warning, or informational message. Default is error.
|
||||
@@ -476,6 +478,14 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
}
|
||||
|
||||
public sendNotebookTelemetryActionEvent(action: TelemetryKeys.TelemetryAction | TelemetryKeys.NbTelemetryAction, additionalProperties: ITelemetryEventProperties = {}): void {
|
||||
let properties: ITelemetryEventProperties = deepClone(additionalProperties);
|
||||
properties['azdata_notebook_guid'] = this.getMetaValue('azdata_notebook_guid');
|
||||
this.adstelemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, action)
|
||||
.withAdditionalProperties(properties)
|
||||
.send();
|
||||
}
|
||||
|
||||
private loadContentMetadata(metadata: INotebookMetadataInternal): void {
|
||||
this._savedKernelInfo = metadata.kernelspec;
|
||||
this._defaultLanguageInfo = metadata.language_info;
|
||||
@@ -491,9 +501,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
if (metadata.azdata_notebook_guid && metadata.azdata_notebook_guid.length === 36) {
|
||||
//Verify if it is actual GUID and then send it to the telemetry
|
||||
if (isUUID(metadata.azdata_notebook_guid)) {
|
||||
this.adstelemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.TelemetryAction.Open)
|
||||
.withAdditionalProperties({ azdata_notebook_guid: metadata.azdata_notebook_guid })
|
||||
.send();
|
||||
this.sendNotebookTelemetryActionEvent(TelemetryKeys.TelemetryAction.Open);
|
||||
}
|
||||
}
|
||||
Object.keys(metadata).forEach(key => {
|
||||
@@ -1162,12 +1170,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
} else if (kernel.info) {
|
||||
this.updateLanguageInfo(kernel.info.language_info);
|
||||
}
|
||||
this.adstelemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.KernelChanged)
|
||||
.withAdditionalProperties({
|
||||
name: kernel.name,
|
||||
alias: kernelAlias || ''
|
||||
})
|
||||
.send();
|
||||
this.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.KernelChanged, {
|
||||
name: kernel.name,
|
||||
alias: kernelAlias || ''
|
||||
});
|
||||
this._kernelChangedEmitter.fire({
|
||||
newValue: kernel,
|
||||
oldValue: undefined,
|
||||
|
||||
Reference in New Issue
Block a user