mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Telemetry points for SQL Database Projects extension (#14088)
* Added publish and schema compare telemetry * Adding telemetry points for add/exclude/delete * telemetry for validation * Adding telemetry from project roundtrip updates, editing sqlproj, and db refs * Changed method for obtaining extension ID during registration * Fixing test failures, updating ads telemetry package dependency * replacing action strings with enums * change database project string actions to enums * Changed action name to better match dialog * PR feedback
This commit is contained in:
@@ -14,7 +14,7 @@ export interface IProjectProviderRegistry {
|
|||||||
* Registers a new project provider
|
* Registers a new project provider
|
||||||
* @param provider The project provider
|
* @param provider The project provider
|
||||||
*/
|
*/
|
||||||
registerProvider(provider: IProjectProvider): vscode.Disposable;
|
registerProvider(provider: IProjectProvider, providerId: string): vscode.Disposable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the providers
|
* Clear the providers
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
import { IProjectProvider } from 'dataworkspace';
|
import { IProjectProvider } from 'dataworkspace';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { IProjectProviderRegistry } from './interfaces';
|
import { IProjectProviderRegistry } from './interfaces';
|
||||||
import { TelemetryReporter, TelemetryViews } from './telemetry';
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from './telemetry';
|
||||||
|
|
||||||
export const ProjectProviderRegistry: IProjectProviderRegistry = new class implements IProjectProviderRegistry {
|
export const ProjectProviderRegistry: IProjectProviderRegistry = new class implements IProjectProviderRegistry {
|
||||||
private _providers = new Array<IProjectProvider>();
|
private _providers = new Array<IProjectProvider>();
|
||||||
private _providerFileExtensionMapping: { [key: string]: IProjectProvider } = {};
|
private _providerFileExtensionMapping: { [key: string]: IProjectProvider } = {};
|
||||||
private _providerProjectTypeMapping: { [key: string]: IProjectProvider } = {};
|
private _providerProjectTypeMapping: { [key: string]: IProjectProvider } = {};
|
||||||
|
|
||||||
registerProvider(provider: IProjectProvider): vscode.Disposable {
|
registerProvider(provider: IProjectProvider, providerId: string): vscode.Disposable {
|
||||||
this.validateProvider(provider);
|
this.validateProvider(provider);
|
||||||
this._providers.push(provider);
|
this._providers.push(provider);
|
||||||
provider.supportedProjectTypes.forEach(projectType => {
|
provider.supportedProjectTypes.forEach(projectType => {
|
||||||
@@ -21,9 +21,9 @@ export const ProjectProviderRegistry: IProjectProviderRegistry = new class imple
|
|||||||
this._providerProjectTypeMapping[projectType.id.toUpperCase()] = provider;
|
this._providerProjectTypeMapping[projectType.id.toUpperCase()] = provider;
|
||||||
});
|
});
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.ProviderRegistration, 'ProviderRegistered')
|
TelemetryReporter.createActionEvent(TelemetryViews.ProviderRegistration, TelemetryActions.ProviderRegistered)
|
||||||
.withAdditionalProperties({
|
.withAdditionalProperties({
|
||||||
providerId: provider.providerExtensionId,
|
providerId: providerId,
|
||||||
extensions: provider.supportedProjectTypes.map(p => p.projectFileExtension).sort().join(', ')
|
extensions: provider.supportedProjectTypes.map(p => p.projectFileExtension).sort().join(', ')
|
||||||
})
|
})
|
||||||
.send();
|
.send();
|
||||||
|
|||||||
@@ -14,13 +14,6 @@ let packageInfo = utils.getPackageInfo(packageJson)!;
|
|||||||
|
|
||||||
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||||
|
|
||||||
export enum TelemetryViews {
|
|
||||||
WorkspaceTreePane = 'WorkspaceTreePane',
|
|
||||||
OpenExistingDialog = 'OpenExistingDialog',
|
|
||||||
NewProjectDialog = 'NewProjectDialog',
|
|
||||||
ProviderRegistration = 'ProviderRegistration'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function calculateRelativity(projectPath: string, workspacePath?: string): string {
|
export function calculateRelativity(projectPath: string, workspacePath?: string): string {
|
||||||
workspacePath = workspacePath ?? vscode.workspace.workspaceFile?.fsPath;
|
workspacePath = workspacePath ?? vscode.workspace.workspaceFile?.fsPath;
|
||||||
|
|
||||||
@@ -42,3 +35,22 @@ export function calculateRelativity(projectPath: string, workspacePath?: string)
|
|||||||
|
|
||||||
return 'other'; // sibling, cousin, descendant, etc.
|
return 'other'; // sibling, cousin, descendant, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export enum TelemetryViews {
|
||||||
|
WorkspaceTreePane = 'WorkspaceTreePane',
|
||||||
|
OpenExistingDialog = 'OpenExistingDialog',
|
||||||
|
NewProjectDialog = 'NewProjectDialog',
|
||||||
|
ProviderRegistration = 'ProviderRegistration'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TelemetryActions {
|
||||||
|
ProviderRegistered = 'ProviderRegistered',
|
||||||
|
ProjectAddedToWorkspace = 'ProjectAddedToWorkspace',
|
||||||
|
ProjectRemovedFromWorkspace = 'ProjectRemovedFromWorkspace',
|
||||||
|
OpeningProject = 'OpeningProject',
|
||||||
|
NewProjectDialogLaunched = 'NewProjectDialogLaunched',
|
||||||
|
OpeningWorkspace = 'OpeningWorkspace',
|
||||||
|
OpenExistingDialogLaunched = 'OpenExistingDialogLaunched',
|
||||||
|
NewProjectDialogCompleted = 'NewProjectDialogCompleted'
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,11 +69,6 @@ declare module 'dataworkspace' {
|
|||||||
* Gets the supported project types
|
* Gets the supported project types
|
||||||
*/
|
*/
|
||||||
readonly supportedProjectTypes: IProjectType[];
|
readonly supportedProjectTypes: IProjectType[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the extension ID for the project provider
|
|
||||||
*/
|
|
||||||
readonly providerExtensionId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { IProjectType } from 'dataworkspace';
|
|||||||
import { directoryExist } from '../common/utils';
|
import { directoryExist } from '../common/utils';
|
||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
|
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
|
||||||
import { TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
class NewProjectDialogModel {
|
class NewProjectDialogModel {
|
||||||
projectTypeId: string = '';
|
projectTypeId: string = '';
|
||||||
@@ -28,7 +28,7 @@ export class NewProjectDialog extends DialogBase {
|
|||||||
super(constants.NewProjectDialogTitle, 'NewProject');
|
super(constants.NewProjectDialogTitle, 'NewProject');
|
||||||
|
|
||||||
// dialog launched from Welcome message button (only visible when no current workspace) vs. "add project" button
|
// dialog launched from Welcome message button (only visible when no current workspace) vs. "add project" button
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.NewProjectDialog, 'NewProjectDialogLaunched')
|
TelemetryReporter.createActionEvent(TelemetryViews.NewProjectDialog, TelemetryActions.NewProjectDialogLaunched)
|
||||||
.withAdditionalProperties({ isWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
.withAdditionalProperties({ isWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ export class NewProjectDialog extends DialogBase {
|
|||||||
try {
|
try {
|
||||||
const validateWorkspace = await this.workspaceService.validateWorkspace();
|
const validateWorkspace = await this.workspaceService.validateWorkspace();
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.NewProjectDialog, 'NewProjectDialogCompleted')
|
TelemetryReporter.createActionEvent(TelemetryViews.NewProjectDialog, TelemetryActions.NewProjectDialogCompleted)
|
||||||
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId, workspaceValidationPassed: validateWorkspace.toString() })
|
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId, workspaceValidationPassed: validateWorkspace.toString() })
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export class NewProjectDialog extends DialogBase {
|
|||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.NewProjectDialog, 'NewProjectDialogErrorThrown')
|
TelemetryReporter.createErrorEvent(TelemetryViews.NewProjectDialog, TelemetryActions.NewProjectDialogCompleted)
|
||||||
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId, error: err?.message ? err.message : err })
|
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId, error: err?.message ? err.message : err })
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import * as constants from '../common/constants';
|
|||||||
import { IWorkspaceService } from '../common/interfaces';
|
import { IWorkspaceService } from '../common/interfaces';
|
||||||
import { fileExist } from '../common/utils';
|
import { fileExist } from '../common/utils';
|
||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
import { calculateRelativity, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
import { calculateRelativity, TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
export class OpenExistingDialog extends DialogBase {
|
export class OpenExistingDialog extends DialogBase {
|
||||||
public _targetTypeRadioCardGroup: azdata.RadioCardGroupComponent | undefined;
|
public _targetTypeRadioCardGroup: azdata.RadioCardGroupComponent | undefined;
|
||||||
@@ -32,7 +32,7 @@ export class OpenExistingDialog extends DialogBase {
|
|||||||
super(constants.OpenExistingDialogTitle, 'OpenProject');
|
super(constants.OpenExistingDialogTitle, 'OpenProject');
|
||||||
|
|
||||||
// dialog launched from Welcome message button (only visible when no current workspace) vs. "add project" button
|
// dialog launched from Welcome message button (only visible when no current workspace) vs. "add project" button
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, 'OpenWorkspaceProjectDialogLaunched')
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.OpenExistingDialogLaunched)
|
||||||
.withAdditionalProperties({ isWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
.withAdditionalProperties({ isWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ export class OpenExistingDialog extends DialogBase {
|
|||||||
try {
|
try {
|
||||||
if (this._targetTypeRadioCardGroup?.selectedCardId === constants.Workspace) {
|
if (this._targetTypeRadioCardGroup?.selectedCardId === constants.Workspace) {
|
||||||
// capture that workspace was selected, also if there's already an open workspace that's being replaced
|
// capture that workspace was selected, also if there's already an open workspace that's being replaced
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, 'OpeningWorkspace')
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.OpeningWorkspace)
|
||||||
.withAdditionalProperties({ hasWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
.withAdditionalProperties({ hasWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export class OpenExistingDialog extends DialogBase {
|
|||||||
addProjectsPromise = this.workspaceService.addProjectsToWorkspace([vscode.Uri.file(this._filePathTextBox!.value!)], vscode.Uri.file(this.workspaceInputBox!.value!));
|
addProjectsPromise = this.workspaceService.addProjectsToWorkspace([vscode.Uri.file(this._filePathTextBox!.value!)], vscode.Uri.file(this.workspaceInputBox!.value!));
|
||||||
}
|
}
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, 'OpeningProject')
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.OpeningProject)
|
||||||
.withAdditionalProperties(telemetryProps)
|
.withAdditionalProperties(telemetryProps)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import * as glob from 'fast-glob';
|
|||||||
import { IWorkspaceService } from '../common/interfaces';
|
import { IWorkspaceService } from '../common/interfaces';
|
||||||
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
||||||
import Logger from '../common/logger';
|
import Logger from '../common/logger';
|
||||||
import { TelemetryReporter, TelemetryViews, calculateRelativity } from '../common/telemetry';
|
import { TelemetryReporter, TelemetryViews, calculateRelativity, TelemetryActions } from '../common/telemetry';
|
||||||
|
|
||||||
const WorkspaceConfigurationName = 'dataworkspace';
|
const WorkspaceConfigurationName = 'dataworkspace';
|
||||||
const ProjectsConfigurationName = 'projects';
|
const ProjectsConfigurationName = 'projects';
|
||||||
@@ -117,7 +117,7 @@ export class WorkspaceService implements IWorkspaceService {
|
|||||||
currentProjects.push(projectFile);
|
currentProjects.push(projectFile);
|
||||||
newProjectFileAdded = true;
|
newProjectFileAdded = true;
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, 'ProjectAddedToWorkspace')
|
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, TelemetryActions.ProjectAddedToWorkspace)
|
||||||
.withAdditionalProperties({
|
.withAdditionalProperties({
|
||||||
workspaceProjectRelativity: calculateRelativity(projectFile.fsPath),
|
workspaceProjectRelativity: calculateRelativity(projectFile.fsPath),
|
||||||
projectType: path.extname(projectFile.fsPath)
|
projectType: path.extname(projectFile.fsPath)
|
||||||
@@ -234,7 +234,7 @@ export class WorkspaceService implements IWorkspaceService {
|
|||||||
if (projectIdx !== -1) {
|
if (projectIdx !== -1) {
|
||||||
currentProjects.splice(projectIdx, 1);
|
currentProjects.splice(projectIdx, 1);
|
||||||
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, 'ProjectRemovedFromWorkspace')
|
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, TelemetryActions.ProjectRemovedFromWorkspace)
|
||||||
.withAdditionalProperties({
|
.withAdditionalProperties({
|
||||||
projectType: path.extname(projectFile.fsPath)
|
projectType: path.extname(projectFile.fsPath)
|
||||||
}).send();
|
}).send();
|
||||||
@@ -290,7 +290,7 @@ export class WorkspaceService implements IWorkspaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (extension.isActive && extension.exports && !ProjectProviderRegistry.providers.includes(extension.exports)) {
|
if (extension.isActive && extension.exports && !ProjectProviderRegistry.providers.includes(extension.exports)) {
|
||||||
ProjectProviderRegistry.registerProvider(extension.exports);
|
ProjectProviderRegistry.registerProvider(extension.exports, extension.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export function createProjectProvider(projectTypes: IProjectType[]): IProjectPro
|
|||||||
const treeDataProvider = new MockTreeDataProvider();
|
const treeDataProvider = new MockTreeDataProvider();
|
||||||
const projectProvider: IProjectProvider = {
|
const projectProvider: IProjectProvider = {
|
||||||
supportedProjectTypes: projectTypes,
|
supportedProjectTypes: projectTypes,
|
||||||
providerExtensionId: 'testProvider',
|
|
||||||
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
|
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
@@ -64,7 +63,7 @@ suite('ProjectProviderRegistry Tests', function (): void {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
||||||
const disposable1 = ProjectProviderRegistry.registerProvider(provider1);
|
const disposable1 = ProjectProviderRegistry.registerProvider(provider1, 'test.testProvider');
|
||||||
let providerResult = ProjectProviderRegistry.getProviderByProjectExtension('testproj');
|
let providerResult = ProjectProviderRegistry.getProviderByProjectExtension('testproj');
|
||||||
should.equal(providerResult, provider1, 'provider1 should be returned for testproj project type');
|
should.equal(providerResult, provider1, 'provider1 should be returned for testproj project type');
|
||||||
// make sure the project type is case-insensitive for getProviderByProjectType method
|
// make sure the project type is case-insensitive for getProviderByProjectType method
|
||||||
@@ -73,7 +72,7 @@ suite('ProjectProviderRegistry Tests', function (): void {
|
|||||||
providerResult = ProjectProviderRegistry.getProviderByProjectExtension('testproj1');
|
providerResult = ProjectProviderRegistry.getProviderByProjectExtension('testproj1');
|
||||||
should.equal(providerResult, provider1, 'provider1 should be returned for testproj1 project type');
|
should.equal(providerResult, provider1, 'provider1 should be returned for testproj1 project type');
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
||||||
const disposable2 = ProjectProviderRegistry.registerProvider(provider2);
|
const disposable2 = ProjectProviderRegistry.registerProvider(provider2, 'test.testProvider2');
|
||||||
providerResult = ProjectProviderRegistry.getProviderByProjectExtension('sqlproj');
|
providerResult = ProjectProviderRegistry.getProviderByProjectExtension('sqlproj');
|
||||||
should.equal(providerResult, provider2, 'provider2 should be returned for sqlproj project type');
|
should.equal(providerResult, provider2, 'provider2 should be returned for sqlproj project type');
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 2, 'there should be 2 project providers at this time');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 2, 'there should be 2 project providers at this time');
|
||||||
@@ -107,7 +106,7 @@ suite('ProjectProviderRegistry Tests', function (): void {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
||||||
ProjectProviderRegistry.registerProvider(provider);
|
ProjectProviderRegistry.registerProvider(provider, 'test.testProvider');
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
||||||
ProjectProviderRegistry.clear();
|
ProjectProviderRegistry.clear();
|
||||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider after clearing the registry');
|
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider after clearing the registry');
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ suite('workspaceTreeDataProvider Tests', function (): void {
|
|||||||
displayName: 'sql project',
|
displayName: 'sql project',
|
||||||
description: ''
|
description: ''
|
||||||
}],
|
}],
|
||||||
providerExtensionId: 'testProvider',
|
|
||||||
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
|
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,11 +5,32 @@
|
|||||||
|
|
||||||
import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry';
|
import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry';
|
||||||
|
|
||||||
import { GetPackageInfo } from './utils';
|
import { getPackageInfo } from './utils';
|
||||||
|
|
||||||
const packageInfo = GetPackageInfo()!;
|
const packageInfo = getPackageInfo()!;
|
||||||
|
|
||||||
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||||
|
|
||||||
|
|
||||||
export enum TelemetryViews {
|
export enum TelemetryViews {
|
||||||
|
ProjectController = 'ProjectController',
|
||||||
|
SqlProjectPublishDialog = 'SqlProjectPublishDialog',
|
||||||
|
ProjectTree = 'ProjectTree'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TelemetryActions {
|
||||||
|
createNewProject = 'createNewProject',
|
||||||
|
addDatabaseReference = 'addDatabaseReference',
|
||||||
|
runStreamingJobValidation = 'runStreamingJobValidation',
|
||||||
|
generateScriptClicked = 'generateScriptClicked',
|
||||||
|
deleteObjectFromProject = 'deleteObjectFromProject',
|
||||||
|
editProjectFile = 'editProjectFile',
|
||||||
|
addItemFromTree = 'addItemFromTree',
|
||||||
|
excludeFromProject = 'excludeFromProject',
|
||||||
|
projectSchemaCompareCommandInvoked = 'projectSchemaCompareCommandInvoked',
|
||||||
|
publishProject = 'publishProject',
|
||||||
|
build = 'build',
|
||||||
|
updateProjectForRoundtrip = 'updateProjectForRoundtrip',
|
||||||
|
changePlatformType = 'changePlatformType',
|
||||||
|
updateSystemDatabaseReferencesInProjFile = 'updateSystemDatabaseReferencesInProjFile'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ export interface IPackageInfo {
|
|||||||
aiKey: string;
|
aiKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetPackageInfo(packageJson?: any): IPackageInfo | undefined {
|
export function getPackageInfo(packageJson?: any): IPackageInfo | undefined {
|
||||||
if (!packageJson) {
|
if (!packageJson) {
|
||||||
packageJson = require('../../package.json');
|
packageJson = require('../../package.json');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { AddDatabaseReferenceDialog } from '../dialogs/addDatabaseReferenceDialo
|
|||||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
||||||
import { DatabaseReferenceTreeItem } from '../models/tree/databaseReferencesTreeItem';
|
import { DatabaseReferenceTreeItem } from '../models/tree/databaseReferencesTreeItem';
|
||||||
import { CreateProjectFromDatabaseDialog } from '../dialogs/createProjectFromDatabaseDialog';
|
import { CreateProjectFromDatabaseDialog } from '../dialogs/createProjectFromDatabaseDialog';
|
||||||
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for managing lifecycle of projects
|
* Controller for managing lifecycle of projects
|
||||||
@@ -38,7 +39,6 @@ export class ProjectsController {
|
|||||||
private netCoreTool: NetCoreTool;
|
private netCoreTool: NetCoreTool;
|
||||||
private buildHelper: BuildHelper;
|
private buildHelper: BuildHelper;
|
||||||
|
|
||||||
projects: Project[] = [];
|
|
||||||
projFileWatchers = new Map<string, vscode.FileSystemWatcher>();
|
projFileWatchers = new Map<string, vscode.FileSystemWatcher>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -57,6 +57,10 @@ export class ProjectsController {
|
|||||||
* @param projectGuid
|
* @param projectGuid
|
||||||
*/
|
*/
|
||||||
public async createNewProject(creationParams: NewProjectParams): Promise<string> {
|
public async createNewProject(creationParams: NewProjectParams): Promise<string> {
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectController, TelemetryActions.createNewProject)
|
||||||
|
.withAdditionalProperties({ template: creationParams.projectTypeId })
|
||||||
|
.send();
|
||||||
|
|
||||||
if (creationParams.projectGuid && !UUID.isUUID(creationParams.projectGuid)) {
|
if (creationParams.projectGuid && !UUID.isUUID(creationParams.projectGuid)) {
|
||||||
throw new Error(`Specified GUID is invalid: '${creationParams.projectGuid}'`);
|
throw new Error(`Specified GUID is invalid: '${creationParams.projectGuid}'`);
|
||||||
}
|
}
|
||||||
@@ -103,6 +107,8 @@ export class ProjectsController {
|
|||||||
public async buildProject(context: Project | dataworkspace.WorkspaceTreeItem): Promise<string> {
|
public async buildProject(context: Project | dataworkspace.WorkspaceTreeItem): Promise<string> {
|
||||||
const project: Project = this.getProjectFromContext(context);
|
const project: Project = this.getProjectFromContext(context);
|
||||||
|
|
||||||
|
const startTime = new Date();
|
||||||
|
|
||||||
// Check mssql extension for project dlls (tracking issue #10273)
|
// Check mssql extension for project dlls (tracking issue #10273)
|
||||||
await this.buildHelper.createBuildDirFolder();
|
await this.buildHelper.createBuildDirFolder();
|
||||||
|
|
||||||
@@ -111,12 +117,21 @@ export class ProjectsController {
|
|||||||
workingDirectory: project.projectFolderPath,
|
workingDirectory: project.projectFolderPath,
|
||||||
argument: this.buildHelper.constructBuildArguments(project.projectFilePath, this.buildHelper.extensionBuildDirPath)
|
argument: this.buildHelper.constructBuildArguments(project.projectFilePath, this.buildHelper.extensionBuildDirPath)
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.netCoreTool.runDotnetCommand(options);
|
await this.netCoreTool.runDotnetCommand(options);
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectController, TelemetryActions.build)
|
||||||
|
.withAdditionalMeasurements({ duration: new Date().getMilliseconds() - startTime.getMilliseconds() })
|
||||||
|
.send();
|
||||||
|
|
||||||
return project.dacpacOutputPath;
|
return project.dacpacOutputPath;
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectController, TelemetryActions.build)
|
||||||
|
.withAdditionalProperties({ error: utils.getErrorMessage(err) })
|
||||||
|
.withAdditionalMeasurements({ duration: new Date().getMilliseconds() - startTime.getMilliseconds() })
|
||||||
|
.send();
|
||||||
|
|
||||||
vscode.window.showErrorMessage(constants.projBuildFailed(utils.getErrorMessage(err)));
|
vscode.window.showErrorMessage(constants.projBuildFailed(utils.getErrorMessage(err)));
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -146,9 +161,22 @@ export class ProjectsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async publishProjectCallback(project: Project, settings: IPublishSettings | IGenerateScriptSettings): Promise<mssql.DacFxResult | undefined> {
|
public async publishProjectCallback(project: Project, settings: IPublishSettings | IGenerateScriptSettings): Promise<mssql.DacFxResult | undefined> {
|
||||||
|
const telemetryProps: Record<string, string> = {};
|
||||||
|
const telemetryMeasures: Record<string, number> = {};
|
||||||
|
const buildStartTime = new Date().getMilliseconds();
|
||||||
|
|
||||||
const dacpacPath = await this.buildProject(project);
|
const dacpacPath = await this.buildProject(project);
|
||||||
|
|
||||||
|
const buildEndTime = new Date().getMilliseconds();
|
||||||
|
telemetryMeasures.buildDuration = buildEndTime - buildStartTime;
|
||||||
|
telemetryProps.buildSucceeded = (dacpacPath !== '').toString();
|
||||||
|
|
||||||
if (!dacpacPath) {
|
if (!dacpacPath) {
|
||||||
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectController, TelemetryActions.publishProject)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.withAdditionalMeasurements(telemetryMeasures)
|
||||||
|
.send();
|
||||||
|
|
||||||
return undefined; // buildProject() handles displaying the error
|
return undefined; // buildProject() handles displaying the error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,12 +186,41 @@ export class ProjectsController {
|
|||||||
|
|
||||||
const dacFxService = await this.getDaxFxService();
|
const dacFxService = await this.getDaxFxService();
|
||||||
|
|
||||||
if ((<IPublishSettings>settings).upgradeExisting) {
|
let result: mssql.DacFxResult;
|
||||||
return await dacFxService.deployDacpac(tempPath, settings.databaseName, (<IPublishSettings>settings).upgradeExisting, settings.connectionUri, azdata.TaskExecutionMode.execute, settings.sqlCmdVariables, settings.deploymentOptions);
|
telemetryProps.profileUsed = (settings.profileUsed ?? false).toString();
|
||||||
}
|
const actionStartTime = new Date().getMilliseconds();
|
||||||
else {
|
|
||||||
return await dacFxService.generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, azdata.TaskExecutionMode.script, settings.sqlCmdVariables, settings.deploymentOptions);
|
try {
|
||||||
|
if ((<IPublishSettings>settings).upgradeExisting) {
|
||||||
|
telemetryProps.publishAction = 'deploy';
|
||||||
|
result = await dacFxService.deployDacpac(tempPath, settings.databaseName, (<IPublishSettings>settings).upgradeExisting, settings.connectionUri, azdata.TaskExecutionMode.execute, settings.sqlCmdVariables, settings.deploymentOptions);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
telemetryProps.publishAction = 'generateScript';
|
||||||
|
result = await dacFxService.generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, azdata.TaskExecutionMode.script, settings.sqlCmdVariables, settings.deploymentOptions);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const actionEndTime = new Date().getMilliseconds();
|
||||||
|
telemetryProps.actionDuration = (actionEndTime - actionStartTime).toString();
|
||||||
|
telemetryProps.totalDuration = (actionEndTime - buildStartTime).toString();
|
||||||
|
telemetryProps.errorMessage = utils.getErrorMessage(err);
|
||||||
|
|
||||||
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectController, TelemetryActions.publishProject)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actionEndTime = new Date().getMilliseconds();
|
||||||
|
telemetryProps.actionDuration = (actionEndTime - actionStartTime).toString();
|
||||||
|
telemetryProps.totalDuration = (actionEndTime - buildStartTime).toString();
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectController, TelemetryActions.publishProject)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async readPublishProfileCallback(profileUri: vscode.Uri): Promise<PublishProfile> {
|
public async readPublishProfileCallback(profileUri: vscode.Uri): Promise<PublishProfile> {
|
||||||
@@ -171,27 +228,42 @@ export class ProjectsController {
|
|||||||
const dacFxService = await this.getDaxFxService();
|
const dacFxService = await this.getDaxFxService();
|
||||||
const profile = await load(profileUri, dacFxService);
|
const profile = await load(profileUri, dacFxService);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
vscode.window.showErrorMessage(constants.profileReadError);
|
vscode.window.showErrorMessage(constants.profileReadError);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async schemaCompare(treeNode: dataworkspace.WorkspaceTreeItem): Promise<void> {
|
public async schemaCompare(treeNode: dataworkspace.WorkspaceTreeItem): Promise<void> {
|
||||||
// check if schema compare extension is installed
|
try {
|
||||||
if (vscode.extensions.getExtension(constants.schemaCompareExtensionId)) {
|
// check if schema compare extension is installed
|
||||||
// build project
|
if (vscode.extensions.getExtension(constants.schemaCompareExtensionId)) {
|
||||||
const dacpacPath = await this.buildProject(treeNode);
|
// build project
|
||||||
|
const dacpacPath = await this.buildProject(treeNode);
|
||||||
|
|
||||||
// check that dacpac exists
|
// check that dacpac exists
|
||||||
if (await utils.exists(dacpacPath)) {
|
if (await utils.exists(dacpacPath)) {
|
||||||
await vscode.commands.executeCommand(constants.schemaCompareStartCommand, dacpacPath);
|
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectController, TelemetryActions.projectSchemaCompareCommandInvoked);
|
||||||
|
await vscode.commands.executeCommand(constants.schemaCompareStartCommand, dacpacPath);
|
||||||
|
} else {
|
||||||
|
throw new Error(constants.buildFailedCannotStartSchemaCompare);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vscode.window.showErrorMessage(constants.buildFailedCannotStartSchemaCompare);
|
throw new Error(constants.schemaCompareNotInstalled);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (err) {
|
||||||
vscode.window.showErrorMessage(constants.schemaCompareNotInstalled);
|
const props: Record<string, string> = {};
|
||||||
|
const message = utils.getErrorMessage(err);
|
||||||
|
|
||||||
|
if (message === constants.buildFailedCannotStartSchemaCompare || message === constants.schemaCompareNotInstalled) {
|
||||||
|
props.errorMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectController, TelemetryActions.projectSchemaCompareCommandInvoked)
|
||||||
|
.withAdditionalProperties(props)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
vscode.window.showErrorMessage(utils.getErrorMessage(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +336,32 @@ export class ProjectsController {
|
|||||||
const newFileText = templates.macroExpansion(itemType.templateScript, { 'OBJECT_NAME': itemObjectName });
|
const newFileText = templates.macroExpansion(itemType.templateScript, { 'OBJECT_NAME': itemObjectName });
|
||||||
const relativeFilePath = path.join(relativePath, itemObjectName + constants.sqlFileExtension);
|
const relativeFilePath = path.join(relativePath, itemObjectName + constants.sqlFileExtension);
|
||||||
|
|
||||||
|
const telemetryProps: Record<string, string> = { itemType: itemType.type };
|
||||||
|
const telemetryMeasurements: Record<string, number> = {};
|
||||||
|
|
||||||
|
if (itemType.type === templates.preDeployScript) {
|
||||||
|
telemetryMeasurements.numPredeployScripts = project.preDeployScripts.length;
|
||||||
|
} else if (itemType.type === templates.postDeployScript) {
|
||||||
|
telemetryMeasurements.numPostdeployScripts = project.postDeployScripts.length;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newEntry = await project.addScriptItem(relativeFilePath, newFileText, itemType.type);
|
const newEntry = await project.addScriptItem(relativeFilePath, newFileText, itemType.type);
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.addItemFromTree)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.withAdditionalMeasurements(telemetryMeasurements)
|
||||||
|
.send();
|
||||||
|
|
||||||
await vscode.commands.executeCommand(constants.vscodeOpenCommand, newEntry.fsUri);
|
await vscode.commands.executeCommand(constants.vscodeOpenCommand, newEntry.fsUri);
|
||||||
treeDataProvider?.notifyTreeDataChanged();
|
treeDataProvider?.notifyTreeDataChanged();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vscode.window.showErrorMessage(utils.getErrorMessage(err));
|
vscode.window.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
|
||||||
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectTree, TelemetryActions.addItemFromTree)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.withAdditionalMeasurements(telemetryMeasurements)
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,8 +372,10 @@ export class ProjectsController {
|
|||||||
const fileEntry = this.getFileProjectEntry(project, node);
|
const fileEntry = this.getFileProjectEntry(project, node);
|
||||||
|
|
||||||
if (fileEntry) {
|
if (fileEntry) {
|
||||||
|
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectTree, TelemetryActions.excludeFromProject);
|
||||||
await project.exclude(fileEntry);
|
await project.exclude(fileEntry);
|
||||||
} else {
|
} else {
|
||||||
|
TelemetryReporter.sendErrorEvent(TelemetryViews.ProjectTree, TelemetryActions.excludeFromProject);
|
||||||
vscode.window.showErrorMessage(constants.unableToPerformAction(constants.excludeAction, node.uri.path));
|
vscode.window.showErrorMessage(constants.unableToPerformAction(constants.excludeAction, node.uri.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +420,16 @@ export class ProjectsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.deleteObjectFromProject)
|
||||||
|
.withAdditionalProperties({ objectType: node.constructor.name })
|
||||||
|
.send();
|
||||||
|
|
||||||
this.refreshProjectsTree(context);
|
this.refreshProjectsTree(context);
|
||||||
} else {
|
} else {
|
||||||
|
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectTree, TelemetryActions.deleteObjectFromProject)
|
||||||
|
.withAdditionalProperties({ objectType: node.constructor.name })
|
||||||
|
.send();
|
||||||
|
|
||||||
vscode.window.showErrorMessage(constants.unableToPerformAction(constants.deleteAction, node.uri.path));
|
vscode.window.showErrorMessage(constants.unableToPerformAction(constants.deleteAction, node.uri.path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,6 +476,9 @@ export class ProjectsController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(project.projectFilePath));
|
await vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(project.projectFilePath));
|
||||||
|
|
||||||
|
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectTree, TelemetryActions.editProjectFile);
|
||||||
|
|
||||||
const projFileWatcher: vscode.FileSystemWatcher = vscode.workspace.createFileSystemWatcher(project.projectFilePath);
|
const projFileWatcher: vscode.FileSystemWatcher = vscode.workspace.createFileSystemWatcher(project.projectFilePath);
|
||||||
this.projFileWatchers.set(project.projectFilePath, projFileWatcher);
|
this.projFileWatchers.set(project.projectFilePath, projFileWatcher);
|
||||||
|
|
||||||
@@ -496,16 +600,25 @@ export class ProjectsController {
|
|||||||
const project: Project = this.getProjectFromContext(node);
|
const project: Project = this.getProjectFromContext(node);
|
||||||
|
|
||||||
let dacpacPath: string = project.dacpacOutputPath;
|
let dacpacPath: string = project.dacpacOutputPath;
|
||||||
|
const preExistingDacpac = await utils.exists(dacpacPath);
|
||||||
|
|
||||||
if (!await utils.exists(dacpacPath)) {
|
const telemetryProps: Record<string, string> = { preExistingDacpac: preExistingDacpac.toString() };
|
||||||
|
|
||||||
|
|
||||||
|
if (!preExistingDacpac) {
|
||||||
dacpacPath = await this.buildProject(project);
|
dacpacPath = await this.buildProject(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
const streamingJobDefinition: string = (await fs.readFile(node.element.fileSystemUri.fsPath)).toString();
|
const streamingJobDefinition: string = (await fs.readFile(node.element.fileSystemUri.fsPath)).toString();
|
||||||
|
|
||||||
const dacFxService = await this.getDaxFxService();
|
const dacFxService = await this.getDaxFxService();
|
||||||
|
const actionStartTime = new Date().getMilliseconds();
|
||||||
|
|
||||||
const result: mssql.ValidateStreamingJobResult = await dacFxService.validateStreamingJob(dacpacPath, streamingJobDefinition);
|
const result: mssql.ValidateStreamingJobResult = await dacFxService.validateStreamingJob(dacpacPath, streamingJobDefinition);
|
||||||
|
|
||||||
|
const duration = new Date().getMilliseconds() - actionStartTime;
|
||||||
|
telemetryProps.success = result.success.toString();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
vscode.window.showInformationMessage(constants.externalStreamingJobValidationPassed);
|
vscode.window.showInformationMessage(constants.externalStreamingJobValidationPassed);
|
||||||
}
|
}
|
||||||
@@ -513,6 +626,11 @@ export class ProjectsController {
|
|||||||
vscode.window.showErrorMessage(result.errorMessage);
|
vscode.window.showErrorMessage(result.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.runStreamingJobValidation)
|
||||||
|
.withAdditionalProperties(telemetryProps)
|
||||||
|
.withAdditionalMeasurements({ duration: duration })
|
||||||
|
.send();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,8 +778,7 @@ export class ProjectsController {
|
|||||||
workspaceApi.showProjectsView();
|
workspaceApi.showProjectsView();
|
||||||
await workspaceApi.addProjectsToWorkspace([vscode.Uri.file(newProjFilePath)], model.newWorkspaceFilePath);
|
await workspaceApi.addProjectsToWorkspace([vscode.Uri.file(newProjFilePath)], model.newWorkspaceFilePath);
|
||||||
}
|
}
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
vscode.window.showErrorMessage(utils.getErrorMessage(err));
|
vscode.window.showErrorMessage(utils.getErrorMessage(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { cssStyles } from '../common/uiConstants';
|
|||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from '../models/IDatabaseReferenceSettings';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
export enum ReferenceType {
|
export enum ReferenceType {
|
||||||
project,
|
project,
|
||||||
@@ -51,7 +52,7 @@ export class AddDatabaseReferenceDialog {
|
|||||||
public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings) => any) | undefined;
|
public addReference: ((proj: Project, settings: ISystemDatabaseReferenceSettings | IDacpacReferenceSettings | IProjectReferenceSettings) => any) | undefined;
|
||||||
|
|
||||||
constructor(private project: Project) {
|
constructor(private project: Project) {
|
||||||
this.dialog = azdata.window.createModelViewDialog(constants.addDatabaseReferenceDialogName);
|
this.dialog = azdata.window.createModelViewDialog(constants.addDatabaseReferenceDialogName, 'addDatabaseReferencesDialog');
|
||||||
this.addDatabaseReferenceTab = azdata.window.createTab(constants.addDatabaseReferenceDialogName);
|
this.addDatabaseReferenceTab = azdata.window.createTab(constants.addDatabaseReferenceDialogName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +158,10 @@ export class AddDatabaseReferenceDialog {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.addDatabaseReference)
|
||||||
|
.withAdditionalProperties({ referenceType: this.currentReferenceType!.toString() })
|
||||||
|
.send();
|
||||||
|
|
||||||
await this.addReference!(this.project, referenceSettings);
|
await this.addReference!(this.project, referenceSettings);
|
||||||
|
|
||||||
this.dispose();
|
this.dispose();
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class CreateProjectFromDatabaseDialog {
|
|||||||
public createProjectFromDatabaseCallback: ((model: ImportDataModel) => any) | undefined;
|
public createProjectFromDatabaseCallback: ((model: ImportDataModel) => any) | undefined;
|
||||||
|
|
||||||
constructor(private profile: azdata.IConnectionProfile | undefined) {
|
constructor(private profile: azdata.IConnectionProfile | undefined) {
|
||||||
this.dialog = azdata.window.createModelViewDialog(constants.createProjectFromDatabaseDialogName);
|
this.dialog = azdata.window.createModelViewDialog(constants.createProjectFromDatabaseDialogName, 'createProjectFromDatabaseDialog');
|
||||||
this.createProjectFromDatabaseTab = azdata.window.createTab(constants.createProjectFromDatabaseDialogName);
|
this.createProjectFromDatabaseTab = azdata.window.createTab(constants.createProjectFromDatabaseDialogName);
|
||||||
this.dialog.registerCloseValidator(async () => {
|
this.dialog.registerCloseValidator(async () => {
|
||||||
return this.validate();
|
return this.validate();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { DeploymentOptions, SchemaObjectType } from '../../../mssql/src/mssql';
|
|||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
import { cssStyles } from '../common/uiConstants';
|
import { cssStyles } from '../common/uiConstants';
|
||||||
import { getConnectionName } from './utils';
|
import { getConnectionName } from './utils';
|
||||||
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
interface DataSourceDropdownValue extends azdata.CategoryValue {
|
interface DataSourceDropdownValue extends azdata.CategoryValue {
|
||||||
dataSource: SqlConnectionDataSource;
|
dataSource: SqlConnectionDataSource;
|
||||||
@@ -40,6 +41,7 @@ export class PublishDatabaseDialog {
|
|||||||
private connectionIsDataSource: boolean | undefined;
|
private connectionIsDataSource: boolean | undefined;
|
||||||
private sqlCmdVars: Record<string, string> | undefined;
|
private sqlCmdVars: Record<string, string> | undefined;
|
||||||
private deploymentOptions: DeploymentOptions | undefined;
|
private deploymentOptions: DeploymentOptions | undefined;
|
||||||
|
private profileUsed: boolean = false;
|
||||||
|
|
||||||
private toDispose: vscode.Disposable[] = [];
|
private toDispose: vscode.Disposable[] = [];
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ export class PublishDatabaseDialog {
|
|||||||
public readPublishProfile: ((profileUri: vscode.Uri) => any) | undefined;
|
public readPublishProfile: ((profileUri: vscode.Uri) => any) | undefined;
|
||||||
|
|
||||||
constructor(private project: Project) {
|
constructor(private project: Project) {
|
||||||
this.dialog = azdata.window.createModelViewDialog(constants.publishDialogName);
|
this.dialog = azdata.window.createModelViewDialog(constants.publishDialogName, 'sqlProjectPublishDialog');
|
||||||
this.publishTab = azdata.window.createTab(constants.publishDialogName);
|
this.publishTab = azdata.window.createTab(constants.publishDialogName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +186,8 @@ export class PublishDatabaseDialog {
|
|||||||
upgradeExisting: true,
|
upgradeExisting: true,
|
||||||
connectionUri: await this.getConnectionUri(),
|
connectionUri: await this.getConnectionUri(),
|
||||||
sqlCmdVariables: this.getSqlCmdVariablesForPublish(),
|
sqlCmdVariables: this.getSqlCmdVariablesForPublish(),
|
||||||
deploymentOptions: await this.getDeploymentOptions()
|
deploymentOptions: await this.getDeploymentOptions(),
|
||||||
|
profileUsed: this.profileUsed
|
||||||
};
|
};
|
||||||
|
|
||||||
azdata.window.closeDialog(this.dialog);
|
azdata.window.closeDialog(this.dialog);
|
||||||
@@ -194,12 +197,15 @@ export class PublishDatabaseDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async generateScriptClick(): Promise<void> {
|
public async generateScriptClick(): Promise<void> {
|
||||||
|
TelemetryReporter.sendActionEvent(TelemetryViews.SqlProjectPublishDialog, TelemetryActions.generateScriptClicked);
|
||||||
|
|
||||||
const sqlCmdVars = this.getSqlCmdVariablesForPublish();
|
const sqlCmdVars = this.getSqlCmdVariablesForPublish();
|
||||||
const settings: IGenerateScriptSettings = {
|
const settings: IGenerateScriptSettings = {
|
||||||
databaseName: this.getTargetDatabaseName(),
|
databaseName: this.getTargetDatabaseName(),
|
||||||
connectionUri: await this.getConnectionUri(),
|
connectionUri: await this.getConnectionUri(),
|
||||||
sqlCmdVariables: sqlCmdVars,
|
sqlCmdVariables: sqlCmdVars,
|
||||||
deploymentOptions: await this.getDeploymentOptions()
|
deploymentOptions: await this.getDeploymentOptions(),
|
||||||
|
profileUsed: this.profileUsed
|
||||||
};
|
};
|
||||||
|
|
||||||
azdata.window.closeDialog(this.dialog);
|
azdata.window.closeDialog(this.dialog);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface IPublishSettings {
|
|||||||
upgradeExisting: boolean;
|
upgradeExisting: boolean;
|
||||||
sqlCmdVariables?: Record<string, string>;
|
sqlCmdVariables?: Record<string, string>;
|
||||||
deploymentOptions?: DeploymentOptions;
|
deploymentOptions?: DeploymentOptions;
|
||||||
|
profileUsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGenerateScriptSettings {
|
export interface IGenerateScriptSettings {
|
||||||
@@ -18,4 +19,5 @@ export interface IGenerateScriptSettings {
|
|||||||
connectionUri: string;
|
connectionUri: string;
|
||||||
sqlCmdVariables?: Record<string, string>;
|
sqlCmdVariables?: Record<string, string>;
|
||||||
deploymentOptions?: DeploymentOptions;
|
deploymentOptions?: DeploymentOptions;
|
||||||
|
profileUsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Uri, window } from 'vscode';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { DataSource } from './dataSources/dataSources';
|
import { DataSource } from './dataSources/dataSources';
|
||||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings';
|
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings';
|
||||||
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing a Project, and providing functions for operating on it
|
* Class representing a Project, and providing functions for operating on it
|
||||||
@@ -195,6 +196,8 @@ export class Project {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectController, TelemetryActions.updateProjectForRoundtrip);
|
||||||
|
|
||||||
if (!this.importedTargets.includes(constants.NetCoreTargets)) {
|
if (!this.importedTargets.includes(constants.NetCoreTargets)) {
|
||||||
const result = await window.showWarningMessage(constants.updateProjectForRoundTrip, constants.yesString, constants.noString);
|
const result = await window.showWarningMessage(constants.updateProjectForRoundTrip, constants.yesString, constants.noString);
|
||||||
if (result === constants.yesString) {
|
if (result === constants.yesString) {
|
||||||
@@ -387,6 +390,13 @@ export class Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.serializeToProjFile(this.projFileXmlDoc);
|
await this.serializeToProjFile(this.projFileXmlDoc);
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.changePlatformType)
|
||||||
|
.withAdditionalProperties({
|
||||||
|
from: this.getProjectTargetVersion(),
|
||||||
|
to: compatLevel
|
||||||
|
})
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -854,6 +864,10 @@ export class Project {
|
|||||||
await this.addSystemDatabaseReference({ databaseName: databaseVariableName, systemDb: systemDb, suppressMissingDependenciesErrors: suppressMissingDependences });
|
await this.addSystemDatabaseReference({ databaseName: databaseVariableName, systemDb: systemDb, suppressMissingDependenciesErrors: suppressMissingDependences });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.ProjectController, TelemetryActions.updateSystemDatabaseReferencesInProjFile)
|
||||||
|
.withAdditionalMeasurements({ referencesCount: this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length })
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addToProjFile(entry: ProjectEntry, xmlTag?: string, attributes?: Map<string, string>): Promise<void> {
|
private async addToProjFile(entry: ProjectEntry, xmlTag?: string, attributes?: Map<string, string>): Promise<void> {
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProje
|
|||||||
import { ProjectsController } from '../controllers/projectController';
|
import { ProjectsController } from '../controllers/projectController';
|
||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||||
import { GetPackageInfo } from '../common/utils';
|
|
||||||
|
|
||||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider {
|
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider {
|
||||||
|
|
||||||
constructor(private projectController: ProjectsController) {
|
constructor(private projectController: ProjectsController) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -76,6 +74,4 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
|
|||||||
|
|
||||||
return vscode.Uri.file(projectFile);
|
return vscode.Uri.file(projectFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
get providerExtensionId(): string { return GetPackageInfo()!.fullName; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ describe('Publish Database Dialog', () => {
|
|||||||
'ProdDatabaseName': 'MyProdDatabase',
|
'ProdDatabaseName': 'MyProdDatabase',
|
||||||
'BackupDatabaseName': 'MyBackupDatabase'
|
'BackupDatabaseName': 'MyBackupDatabase'
|
||||||
},
|
},
|
||||||
deploymentOptions: mockDacFxOptionsResult.deploymentOptions
|
deploymentOptions: mockDacFxOptionsResult.deploymentOptions,
|
||||||
|
profileUsed: false
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.object.publish = (_, prof) => { profile = prof; };
|
dialog.object.publish = (_, prof) => { profile = prof; };
|
||||||
@@ -94,7 +95,8 @@ describe('Publish Database Dialog', () => {
|
|||||||
'ProdDatabaseName': 'MyProdDatabase',
|
'ProdDatabaseName': 'MyProdDatabase',
|
||||||
'BackupDatabaseName': 'MyBackupDatabase'
|
'BackupDatabaseName': 'MyBackupDatabase'
|
||||||
},
|
},
|
||||||
deploymentOptions: mockDacFxOptionsResult.deploymentOptions
|
deploymentOptions: mockDacFxOptionsResult.deploymentOptions,
|
||||||
|
profileUsed: false
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.object.generateScript = (_, prof) => { profile = prof; };
|
dialog.object.generateScript = (_, prof) => { profile = prof; };
|
||||||
|
|||||||
Reference in New Issue
Block a user