Fixes for sql db projects dashboard (#15100)

* Fix ID assignment for sql db projects dashboard

* Update with new fixes

* Fix tests

* Fix test
This commit is contained in:
Sakshi Sharma
2021-05-05 19:00:22 -07:00
committed by GitHub
parent 8c08c79d67
commit 3b3df2c3fc
9 changed files with 84 additions and 57 deletions

View File

@@ -37,12 +37,14 @@ export const buildAction = localize('buildAction', "Build");
export const publishAction = localize('publishAction', "Publish");
export const changeTargetPlatformAction = localize('changeTargetPlatformAction', "Change Target Platform");
export const ID = localize('ID', "ID");
export const Status = localize('Status', "Status");
export const Time = localize('Time', "Time");
export const Date = localize('Date', "Date");
export const Builds = localize('Builds', "Builds");
export const Deployments = localize('Deployments', "Deployments");
export const TargetPlatform = localize('TargetPlatform', "Target Platform");
export const TargetServer = localize('TargetServer', "Target Server");
export const TargetDatabase = localize('TargetDatabase', "Target Database");
export const BuildHistory = localize('BuildHistory', "Build History");
export const PublishHistory = localize('PublishHistory', "Publish History");
export const Success = localize('Success', "Success");
export const Failed = localize('Failed', "Failed");

View File

@@ -32,7 +32,7 @@ import { DatabaseReferenceTreeItem } from '../models/tree/databaseReferencesTree
import { CreateProjectFromDatabaseDialog } from '../dialogs/createProjectFromDatabaseDialog';
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
import { IconPathHelper } from '../common/iconHelper';
import { DashboardData, Status } from '../models/dashboardData/dashboardData';
import { DashboardData, PublishData, Status } from '../models/dashboardData/dashboardData';
const maxTableLength = 10;
@@ -43,7 +43,7 @@ export class ProjectsController {
private netCoreTool: NetCoreTool;
private buildHelper: BuildHelper;
private buildInfo: DashboardData[] = [];
private deployInfo: DashboardData[] = [];
private publishInfo: PublishData[] = [];
projFileWatchers = new Map<string, vscode.FileSystemWatcher>();
@@ -52,18 +52,17 @@ export class ProjectsController {
this.buildHelper = new BuildHelper();
}
public getDashboardDeployData(projectFile: string): (string | dataworkspace.IconCellValue)[][] {
public getDashboardPublishData(projectFile: string): (string | dataworkspace.IconCellValue)[][] {
const infoRows: (string | dataworkspace.IconCellValue)[][] = [];
let count = 0;
for (let i = this.deployInfo.length - 1; i >= 0; i--) {
if (this.deployInfo[i].projectFile === projectFile) {
for (let i = this.publishInfo.length - 1; i >= 0; i--) {
if (this.publishInfo[i].projectFile === projectFile) {
let icon: azdata.IconPath;
let text: string;
if (this.deployInfo[i].status === Status.success) {
if (this.publishInfo[i].status === Status.success) {
icon = IconPathHelper.success;
text = constants.Success;
} else if (this.deployInfo[i].status === Status.failed) {
} else if (this.publishInfo[i].status === Status.failed) {
icon = IconPathHelper.error;
text = constants.Failed;
} else {
@@ -71,13 +70,13 @@ export class ProjectsController {
text = constants.InProgress;
}
let infoRow: (string | dataworkspace.IconCellValue)[] = [count.toString(),
{ text: text, icon: icon },
this.deployInfo[i].target,
this.deployInfo[i].timeToCompleteAction,
this.deployInfo[i].startDate];
let infoRow: (string | dataworkspace.IconCellValue)[] = [{ text: text, icon: icon },
this.publishInfo[i].startDate,
this.publishInfo[i].timeToCompleteAction,
this.publishInfo[i].target,
this.publishInfo[i].targetServer,
this.publishInfo[i].targetDatabase];
infoRows.push(infoRow);
count++;
}
}
@@ -86,7 +85,6 @@ export class ProjectsController {
public getDashboardBuildData(projectFile: string): (string | dataworkspace.IconCellValue)[][] {
const infoRows: (string | dataworkspace.IconCellValue)[][] = [];
let count = 0;
for (let i = this.buildInfo.length - 1; i >= 0; i--) {
if (this.buildInfo[i].projectFile === projectFile) {
@@ -103,13 +101,11 @@ export class ProjectsController {
text = constants.InProgress;
}
let infoRow: (string | dataworkspace.IconCellValue)[] = [count.toString(),
{ text: text, icon: icon },
this.buildInfo[i].target,
let infoRow: (string | dataworkspace.IconCellValue)[] = [{ text: text, icon: icon },
this.buildInfo[i].startDate,
this.buildInfo[i].timeToCompleteAction,
this.buildInfo[i].startDate];
this.buildInfo[i].target];
infoRows.push(infoRow);
count++;
}
}
@@ -278,13 +274,13 @@ export class ProjectsController {
telemetryProps.profileUsed = (settings.profileUsed ?? false).toString();
const currentDate = new Date();
const actionStartTime = currentDate.getTime();
const currentDeployTimeInfo = `${currentDate.toLocaleDateString()} ${constants.at} ${currentDate.toLocaleTimeString()}`;
const currentPublishTimeInfo = `${currentDate.toLocaleDateString()} ${constants.at} ${currentDate.toLocaleTimeString()}`;
let deployInfoNew = new DashboardData(project.projectFilePath, Status.inProgress, project.getProjectTargetVersion(), currentDeployTimeInfo);
this.deployInfo.push(deployInfoNew);
let publishInfoNew = new PublishData(project.projectFilePath, Status.inProgress, project.getProjectTargetVersion(), currentPublishTimeInfo, settings.databaseName, settings.serverName);
this.publishInfo.push(publishInfoNew);
if (this.deployInfo.length - 1 === maxTableLength) {
this.deployInfo.shift(); // Remove the first element to maintain the length
if (this.publishInfo.length - 1 === maxTableLength) {
this.publishInfo.shift(); // Remove the first element to maintain the length
}
try {
@@ -298,29 +294,29 @@ export class ProjectsController {
}
} catch (err) {
const actionEndTime = new Date().getTime();
const timeToFailureDeploy = actionEndTime - actionStartTime;
telemetryProps.actionDuration = timeToFailureDeploy.toString();
const timeToFailurePublish = actionEndTime - actionStartTime;
telemetryProps.actionDuration = timeToFailurePublish.toString();
telemetryProps.totalDuration = (actionEndTime - buildStartTime).toString();
TelemetryReporter.createErrorEvent(TelemetryViews.ProjectController, TelemetryActions.publishProject)
.withAdditionalProperties(telemetryProps)
.send();
const currentDeployIndex = this.deployInfo.findIndex(d => d.startDate === currentDeployTimeInfo);
this.deployInfo[currentDeployIndex].status = Status.failed;
this.deployInfo[currentDeployIndex].timeToCompleteAction = utils.timeConversion(timeToFailureDeploy);
const currentPublishIndex = this.publishInfo.findIndex(d => d.startDate === currentPublishTimeInfo);
this.publishInfo[currentPublishIndex].status = Status.failed;
this.publishInfo[currentPublishIndex].timeToCompleteAction = utils.timeConversion(timeToFailurePublish);
throw err;
}
const actionEndTime = new Date().getTime();
const timeToDeploy = actionEndTime - actionStartTime;
telemetryProps.actionDuration = timeToDeploy.toString();
const timeToPublish = actionEndTime - actionStartTime;
telemetryProps.actionDuration = timeToPublish.toString();
telemetryProps.totalDuration = (actionEndTime - buildStartTime).toString();
const currentDeployIndex = this.deployInfo.findIndex(d => d.startDate === currentDeployTimeInfo);
this.deployInfo[currentDeployIndex].status = result.success ? Status.success : Status.failed;
this.deployInfo[currentDeployIndex].timeToCompleteAction = utils.timeConversion(timeToDeploy);
const currentPublishIndex = this.publishInfo.findIndex(d => d.startDate === currentPublishTimeInfo);
this.publishInfo[currentPublishIndex].status = result.success ? Status.success : Status.failed;
this.publishInfo[currentPublishIndex].timeToCompleteAction = utils.timeConversion(timeToPublish);
TelemetryReporter.createActionEvent(TelemetryViews.ProjectController, TelemetryActions.publishProject)
.withAdditionalProperties(telemetryProps)

View File

@@ -42,6 +42,7 @@ export class PublishDatabaseDialog {
private sqlCmdVars: Record<string, string> | undefined;
private deploymentOptions: DeploymentOptions | undefined;
private profileUsed: boolean = false;
private serverName: string | undefined;
private toDispose: vscode.Disposable[] = [];
@@ -183,6 +184,7 @@ export class PublishDatabaseDialog {
public async publishClick(): Promise<void> {
const settings: IPublishSettings = {
databaseName: this.getTargetDatabaseName(),
serverName: this.getServerName(),
upgradeExisting: true,
connectionUri: await this.getConnectionUri(),
sqlCmdVariables: this.getSqlCmdVariablesForPublish(),
@@ -202,6 +204,7 @@ export class PublishDatabaseDialog {
const sqlCmdVars = this.getSqlCmdVariablesForPublish();
const settings: IGenerateScriptSettings = {
databaseName: this.getTargetDatabaseName(),
serverName: this.getServerName(),
connectionUri: await this.getConnectionUri(),
sqlCmdVariables: sqlCmdVars,
deploymentOptions: await this.getDeploymentOptions(),
@@ -249,6 +252,10 @@ export class PublishDatabaseDialog {
return this.project.projectFileName;
}
public getServerName(): string {
return this.serverName!;
}
private createRadioButtons(view: azdata.ModelView): azdata.Component {
this.connectionsRadioButton = view.modelBuilder.radioButton()
.withProperties({
@@ -488,6 +495,7 @@ export class PublishDatabaseDialog {
selectConnectionButton.onDidClick(async () => {
let connection = await azdata.connection.openConnectionDialog();
this.connectionId = connection.connectionId;
this.serverName = connection.options['server'];
let connectionTextboxValue: string = getConnectionName(connection);
@@ -550,6 +558,7 @@ export class PublishDatabaseDialog {
(<azdata.DropDownComponent>this.targetDatabaseDropDown).values = [];
this.connectionId = result.connectionId;
this.serverName = result.serverName;
await this.updateConnectionComponents(result.connection, <string>this.connectionId);
if (result.databaseName) {

View File

@@ -7,6 +7,7 @@ import { DeploymentOptions } from '../../../mssql/src/mssql';
export interface IPublishSettings {
databaseName: string;
serverName: string;
connectionUri: string;
upgradeExisting: boolean;
sqlCmdVariables?: Record<string, string>;
@@ -16,6 +17,7 @@ export interface IPublishSettings {
export interface IGenerateScriptSettings {
databaseName: string;
serverName: string;
connectionUri: string;
sqlCmdVariables?: Record<string, string>;
deploymentOptions?: DeploymentOptions;

View File

@@ -19,6 +19,17 @@ export class DashboardData {
}
}
export class PublishData extends DashboardData {
public targetServer: string;
public targetDatabase: string;
constructor(projectFile: string, status: Status, target: string, startDate: string, targetDatabase: string, targetServer: string) {
super(projectFile, status, target, startDate);
this.targetDatabase = targetDatabase;
this.targetServer = targetServer;
}
}
export enum Status {
success,
failed,

View File

@@ -16,6 +16,7 @@ import { SqlConnectionDataSource } from '../dataSources/sqlConnectionStringSourc
// only reading db name, connection string, and SQLCMD vars from profile for now
export interface PublishProfile {
databaseName: string;
serverName: string;
connectionId: string;
connection: string;
sqlCmdVariables: Record<string, string>;
@@ -45,6 +46,7 @@ export async function load(profileUri: Uri, dacfxService: mssql.IDacFxService):
return {
databaseName: targetDbName,
serverName: connectionInfo.server,
connectionId: connectionInfo.connectionId,
connection: connectionInfo.connection,
sqlCmdVariables: sqlCmdVariables,
@@ -52,14 +54,14 @@ export async function load(profileUri: Uri, dacfxService: mssql.IDacFxService):
};
}
async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connection: string }> {
async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connection: string, server: string }> {
let targetConnection: string = '';
let connId: string = '';
let server: string = '';
if (xmlDoc.documentElement.getElementsByTagName(constants.targetConnectionString).length > 0) {
const targetConnectionString = xmlDoc.documentElement.getElementsByTagName(constants.TargetConnectionString)[0].textContent;
const dataSource = new SqlConnectionDataSource('', targetConnectionString);
let server: string = '';
let username: string = '';
const connectionProfile = dataSource.getConnectionProfile();
@@ -86,6 +88,7 @@ async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string
return {
connectionId: connId,
connection: targetConnection
connection: targetConnection,
server: server
};
}

View File

@@ -127,27 +127,28 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
* Gets the data to be displayed in the project dashboard
*/
getDashboardComponents(projectFile: string): dataworkspace.IDashboardTable[] {
const deployInfo: dataworkspace.IDashboardTable = {
name: constants.Deployments,
columns: [{ displayName: constants.ID, width: 100 },
{ displayName: constants.Status, width: 250, type: 'icon' },
{ displayName: constants.Target, width: 250 },
{ displayName: constants.Time, width: 250 },
{ displayName: constants.Date, width: 250 }],
data: this.projectController.getDashboardDeployData(projectFile)
const width = 200;
const publishInfo: dataworkspace.IDashboardTable = {
name: constants.PublishHistory,
columns: [{ displayName: constants.Status, width: width, type: 'icon' },
{ displayName: constants.Date, width: width },
{ displayName: constants.Time, width: width },
{ displayName: constants.TargetPlatform, width: width },
{ displayName: constants.TargetServer, width: width },
{ displayName: constants.TargetDatabase, width: width }],
data: this.projectController.getDashboardPublishData(projectFile)
};
const buildInfo: dataworkspace.IDashboardTable = {
name: constants.Builds,
columns: [{ displayName: constants.ID, width: 100 },
{ displayName: constants.Status, width: 250, type: 'icon' },
{ displayName: constants.Target, width: 250 },
{ displayName: constants.Time, width: 250 },
{ displayName: constants.Date, width: 250 }],
name: constants.BuildHistory,
columns: [{ displayName: constants.Status, width: width, type: 'icon' },
{ displayName: constants.Date, width: width },
{ displayName: constants.Time, width: width },
{ displayName: constants.TargetPlatform, width: width }],
data: this.projectController.getDashboardBuildData(projectFile)
};
return [deployInfo, buildInfo];
return [publishInfo, buildInfo];
}
get image(): ThemedIconPath {

View File

@@ -67,12 +67,14 @@ describe('Publish Database Dialog', () => {
dialog.setup(x => x.getTargetDatabaseName()).returns(() => 'MockDatabaseName');
dialog.setup(x => x.getSqlCmdVariablesForPublish()).returns(() => proj.sqlCmdVariables);
dialog.setup(x => x.getDeploymentOptions()).returns(() => { return Promise.resolve(mockDacFxOptionsResult.deploymentOptions); });
dialog.setup(x => x.getServerName()).returns(() => 'MockServer');
dialog.callBase = true;
let profile: IPublishSettings | IGenerateScriptSettings | undefined;
const expectedPublish: IPublishSettings = {
databaseName: 'MockDatabaseName',
serverName: 'MockServer',
connectionUri: 'Mock|Connection|Uri',
upgradeExisting: true,
sqlCmdVariables: {
@@ -90,6 +92,7 @@ describe('Publish Database Dialog', () => {
const expectedGenScript: IGenerateScriptSettings = {
databaseName: 'MockDatabaseName',
serverName: 'MockServer',
connectionUri: 'Mock|Connection|Uri',
sqlCmdVariables: {
'ProdDatabaseName': 'MyProdDatabase',

View File

@@ -411,7 +411,7 @@ describe('ProjectsController', function (): void {
const proj = await testUtils.createTestProject(baselines.openProjectFileBaseline);
await projController.object.publishProjectCallback(proj, { connectionUri: '', databaseName: '' });
await projController.object.publishProjectCallback(proj, { connectionUri: '', databaseName: '' , serverName: ''});
should(builtDacpacPath).not.equal('', 'built dacpac path should be set');
should(publishedDacpacPath).not.equal('', 'published dacpac path should be set');