Enabling Files tab to the database properties (#24138)

* initial changes for loadin dsc table with real values from smo

* Displaying diff columns for DSC for diff sql server

* checkbox maiants the selection

* elevate option fails to load correct value when set to when_supported option

* all working till maxdop, todo pause option, save

* commented MAXDOP changes, as it is causing issues

* primary,sec,checkbox working as expected, TODO:MaxDop etc options,saving,tests

* Undo MAXDOP commented code

* refactored with service data

* column header width adjustments

* Maxdop and pause resume options completed, apply button is failing now

* Removed option names from loc  and using Id instead as names may change in future like in doc

* Apply button fixed

* refactored to reduce table reload

* Ledger digest completed

* refactor done: maxdop secondary shows wrong data from pause_resume

* refactor more: all working but table focus disturbs on update table

* adds conditions for unsupported dsc to <2016 server

* maxdop secondary checkbox fix

* rows still loses focus after value change due to update table row data

* Fixed updating secondary dropdown value

* reusing the private method and removed the duplicated codes

* initial commit - fullText and owner need revision

* Enter key in input type allows the change to update the table data, reduces the live update issues

* Setting focus to the current row

* loading data, need stylings-increase col length, etc

* using the existed setTableData method

* Adding new file dialog

* creating addFile, but not displaying in table, issue with appendData

* Adding row to the table, options are getting from STS

* all working except InPercent value

* code review comment updates

* Input type checkbox update table additional validation

* all except path

* fixing the input type focus and reverting the enterKeyPress logic

* browse path is created, need stylings,refactor,filestream selection and add

* fixing the flickering issue with data refresh

* new file options toggle and grid display string updates

* moving code inline and using actual component

* cleanup

* Add file saving is done, except one styling issue with autogrowth section

* add,remove working, need to edit file

* add, edit, remove - all working, need css fixes and -1 fix

* addressing code review comments

* adding local changes adn fixing for edit file

* adjusting css

* addressing code review comment for using loc var instead of duplicated line of code to get the rowinfo

* all fixed, need testing and refactor

* vBump STS  and fixing required field causing the apply button not enable for other options on main branch

* fixing autogrowth radio buttons change updates

* all working except some css

* disabled size for filestream

* fixing filegroups and filetypes scnearios, added filename validation for newfile, todo:editingNew file

* added max and min values to the inputs

* editing filename validation completed, all done exccept CSS

* all fixed except scroll bar

* edit db file header, filename enable issue fix

* PR comment supporting updates for STS

* min updates

* modfying addButtonsForTable method and reusing it for edit button

* code review comment updates

* Dialogbase addbuttons to table refactored

* more typo fixes

* removing fulltext index prop

* service fix

* using path.join instead of hardcoded separators

* final commit changes
This commit is contained in:
Sai Avishkar Sreerama
2023-08-29 14:42:09 -05:00
committed by GitHub
parent 9557e77982
commit c4b1765745
12 changed files with 795 additions and 65 deletions

View File

@@ -5,14 +5,15 @@
import * as azdata from 'azdata';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { DefaultInputWidth, DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
import { DefaultInputWidth, DefaultTableWidth, DefaultMinTableRowCount, DefaultMaxTableRowCount, getTableHeight, DialogButton } from '../../ui/dialogBase';
import { IObjectManagementService } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl } from '../constants';
import { Database, DatabaseScopedConfigurationsInfo, DatabaseViewInfo } from '../interfaces';
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseFilesPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl } from '../constants';
import { Database, DatabaseFile, DatabaseScopedConfigurationsInfo, DatabaseViewInfo, FileGrowthType } from '../interfaces';
import { convertNumToTwoDecimalStringInMB } from '../utils';
import { isUndefinedOrNull } from '../../types';
import { deepClone } from '../../util/objects';
import { DatabaseFileDialog } from './databaseFileDialog';
const MAXDOP_Max_Limit = 32767;
const PAUSED_RESUMABLE_INDEX_Max_Limit = 71582;
@@ -21,6 +22,7 @@ const DscTableRowLength = 15;
export class DatabaseDialog extends ObjectManagementDialogBase<Database, DatabaseViewInfo> {
// Database Properties tabs
private generalTab: azdata.Tab;
private filesTab: azdata.Tab;
private optionsTab: azdata.Tab;
private dscTab: azdata.Tab;
private optionsTabSectionsContainer: azdata.Component[] = [];
@@ -43,6 +45,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
private memoryAllocatedInput: azdata.InputBoxComponent;
private memoryUsedInput: azdata.InputBoxComponent;
private collationInput: azdata.InputBoxComponent;
// Files Tab
private readonly filesTabId: string = 'filesDatabaseId';
private databaseFilesTable: azdata.TableComponent;
// Options Tab
private readonly optionsTabId: string = 'optionsDatabaseId';
private autoCreateIncrementalStatisticsInput: azdata.CheckBoxComponent;
@@ -91,6 +96,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
case this.generalTabId:
helpUrl = DatabaseGeneralPropertiesDocUrl;
break;
case this.filesTabId:
helpUrl = DatabaseFilesPropertiesDocUrl;
break;
case this.optionsTabId:
helpUrl = DatabaseOptionsPropertiesDocUrl;
break;
@@ -113,11 +121,11 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
}
this.formContainer.addItems(components);
} else {
// Initilaize general Tab sections
// Initialize general Tab sections
this.initializeBackupSection();
this.initializeDatabaseSection();
//Initilaize options Tab sections
//Initialize options Tab sections
this.initializeOptionsGeneralSection();
this.initializeAutomaticSection();
if (!isUndefinedOrNull(this.objectInfo.isLedgerDatabase)) {
@@ -130,7 +138,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
const tabs: azdata.Tab[] = [];
// Initilaize general Tab
// Initialize general Tab
this.generalTab = {
title: localizedConstants.GeneralSectionHeader,
id: this.generalTabId,
@@ -141,7 +149,20 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
};
tabs.push(this.generalTab);
// Initilaize Options Tab
// Initialize Files Tab
// Files tab is only enabled for SQL Server properties view
if (!isUndefinedOrNull(this.objectInfo.isFilesTabSupported)) {
const filesGeneralSection = this.initializeFilesGeneralSection();
const databaseFilesSection = this.initializeDatabaseFilesSection();
this.filesTab = {
title: localizedConstants.FilesSectionHeader,
id: this.filesTabId,
content: this.createGroup('', [filesGeneralSection, databaseFilesSection], false)
};
tabs.push(this.filesTab);
}
// Initialize Options Tab
this.optionsTab = {
title: localizedConstants.OptionsSectionHeader,
id: this.optionsTabId,
@@ -149,7 +170,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
};
tabs.push(this.optionsTab);
// Initilaize DSC Tab section
// Initialize DSC Tab section
if (!isUndefinedOrNull(this.objectInfo.databaseScopedConfigurations)) {
await this.initializeDatabaseScopedConfigurationSection();
this.dscTabSectionsContainer.push(await this.initializeDscValueDropdownTypeSection())
@@ -162,7 +183,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
tabs.push(this.dscTab);
}
// Initilaize tab group with tabbed panel
// Initialize tab group with tabbed panel
const propertiesTabGroup = { title: '', tabs: tabs };
const propertiesTabbedPannel = this.modelView.modelBuilder.tabbedPanel()
.withTabs([propertiesTabGroup])
@@ -396,6 +417,216 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
}
//#endregion
//#region Database Properties - Files Tab
private initializeFilesGeneralSection(): azdata.GroupContainer {
let containers: azdata.Component[] = [];
// Database name
this.nameInput = this.createInputBox(async () => { }, {
ariaLabel: localizedConstants.DatabaseNameText,
inputType: 'text',
enabled: this.options.isNewObject,
value: this.objectInfo.name
});
containers.push(this.createLabelInputContainer(localizedConstants.DatabaseNameText, this.nameInput));
// Owner
let loginNames = this.viewInfo.loginNames?.options;
if (loginNames?.length > 0) {
// Removing <default> login name from the list and adding current owner if not exists
if (!this.viewInfo.loginNames?.options.find(owner => owner === this.objectInfo.owner)) {
loginNames[0] = this.objectInfo.owner;
} else {
loginNames.splice(0, 1);
}
let ownerDropbox = this.createDropdown(localizedConstants.OwnerText, async () => {
this.objectInfo.owner = ownerDropbox.value as string;
}, loginNames, this.objectInfo.owner);
containers.push(this.createLabelInputContainer(localizedConstants.OwnerText, ownerDropbox));
}
return this.createGroup('', containers, false);
}
private initializeDatabaseFilesSection(): azdata.GroupContainer {
this.databaseFilesTable = this.modelView.modelBuilder.table().withProps({
columns: [{
type: azdata.ColumnType.text,
value: localizedConstants.LogicalNameText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.FileTypeText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.FilegroupText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.SizeInMbText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.AutogrowthMaxsizeText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.PathText
}, {
type: azdata.ColumnType.text,
value: localizedConstants.FileNameText
}],
data: this.objectInfo.files?.map(file => {
return this.convertToDataView(file);
}),
height: getTableHeight(this.objectInfo.files?.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
width: DefaultTableWidth,
forceFitColumns: azdata.ColumnSizingMode.DataFit,
CSSStyles: {
'margin-left': '10px'
}
}).component();
const addButtonComponent: DialogButton = {
buttonAriaLabel: localizedConstants.AddButton,
buttonHandler: (button) => this.onAddDatabaseFilesButtonClicked(button)
};
const removeButtonComponent: DialogButton = {
buttonAriaLabel: localizedConstants.RemoveButton,
buttonHandler: () => this.onRemoveDatabaseFilesButtonClicked()
};
const editbuttonComponent: DialogButton = {
buttonAriaLabel: localizedConstants.EditButton,
buttonHandler: (button) => this.onEditDatabaseFilesButtonClicked(button)
};
const databaseFilesButtonContainer = this.addButtonsForTable(this.databaseFilesTable, addButtonComponent, removeButtonComponent, editbuttonComponent);
return this.createGroup(localizedConstants.DatabaseFilesText, [this.databaseFilesTable, databaseFilesButtonContainer], true);
}
/**
* Converts the file object to a data view object
* @param file database file object
* @returns data view object
*/
private convertToDataView(file: DatabaseFile): any[] {
return [
file.name,
file.type,
file.fileGroup,
file.sizeInMb,
file.isAutoGrowthEnabled ? localizedConstants.AutoGrowthValueStringGenerator(file.type !== localizedConstants.FilestreamFileType
, file.autoFileGrowth.toString()
, file.autoFileGrowthType === FileGrowthType.Percent
, file.maxSizeLimitInMb) : localizedConstants.NoneText,
file.path,
file.fileNameWithExtension
];
}
private async onAddDatabaseFilesButtonClicked(button: azdata.ButtonComponent): Promise<void> {
// Open file dialog to create file
const result = await this.openDatabaseFileDialog(button);
if (!isUndefinedOrNull(result)) {
this.objectInfo.files?.push(result);
var newData = this.objectInfo.files?.map(file => {
return this.convertToDataView(file);
});
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
}
}
private async onEditDatabaseFilesButtonClicked(button: azdata.ButtonComponent): Promise<void> {
if (this.databaseFilesTable.selectedRows.length === 1) {
const result = await this.openDatabaseFileDialog(button);
if (!isUndefinedOrNull(result)) {
this.objectInfo.files[this.databaseFilesTable.selectedRows[0]] = result;
var newData = this.objectInfo.files?.map(file => {
return this.convertToDataView(file);
});
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
}
}
}
/**
* Removes the selected database file from the table
*/
private async onRemoveDatabaseFilesButtonClicked(): Promise<void> {
if (this.databaseFilesTable.selectedRows.length === 1) {
this.objectInfo.files?.splice(this.databaseFilesTable.selectedRows[0], 1);
var newData = this.objectInfo.files?.map(file => {
return this.convertToDataView(file);
});
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
}
}
/**
* Validate the selected row to enable/disable the remove button
* @returns true if the remove button should be enabled, false otherwise
*/
protected override get removeButtonEnabled(): boolean {
let isEnabled = true;
if (this.databaseFilesTable.selectedRows !== undefined) {
const selectedRowId = this.objectInfo.files[this.databaseFilesTable.selectedRows[0]].id;
// Cannot delete a Primary row data file, Id is always 1.
if (this.databaseFilesTable.selectedRows.length === 1 && selectedRowId === 1) {
isEnabled = false;
}
// Cannot remove a log file if there are no other log files, LogFiletype is always a Log file type
else if (this.objectInfo.files[this.databaseFilesTable.selectedRows[0]].type === localizedConstants.LogFiletype) {
isEnabled = false;
this.objectInfo.files.forEach(file => {
if (file.id !== selectedRowId && file.type === localizedConstants.LogFiletype) {
isEnabled = true;
}
});
}
}
return isEnabled;
}
private async openDatabaseFileDialog(button: azdata.ButtonComponent): Promise<DatabaseFile> {
const defaultFileSizeInMb: number = 8
const defaultFileGrowthInMb: number = 64
const defaultFileGrowthInPercent: number = 10;
const defaultMaxFileSizeLimitedToInMb: number = 100;
const selectedFile = this.databaseFilesTable.selectedRows !== undefined ? this.objectInfo.files[this.databaseFilesTable?.selectedRows[0]] : undefined;
if (!isUndefinedOrNull(selectedFile) && selectedFile.type === localizedConstants.FilestreamFileType) {
selectedFile.autoFileGrowth = defaultFileGrowthInMb;
}
const isNewFile: boolean = button.ariaLabel === localizedConstants.AddButton;
const isEditingNewFile: boolean = button.ariaLabel === localizedConstants.EditButton && selectedFile.id === undefined;
const databaseFile: DatabaseFile = isNewFile ? {
id: undefined,
name: '',
type: localizedConstants.RowsDataFileType,
path: this.objectInfo.files[0].path,
fileGroup: this.viewInfo.rowDataFileGroupsOptions[0],
fileNameWithExtension: '',
sizeInMb: defaultFileSizeInMb,
isAutoGrowthEnabled: true,
autoFileGrowth: defaultFileGrowthInMb,
autoFileGrowthType: FileGrowthType.KB,
maxSizeLimitInMb: defaultMaxFileSizeLimitedToInMb
} : selectedFile;
const dialog = new DatabaseFileDialog({
title: (isNewFile || isEditingNewFile) ? localizedConstants.AddDatabaseFilesText : localizedConstants.EditDatabaseFilesText(databaseFile.name),
viewInfo: this.viewInfo,
files: this.objectInfo.files,
isNewFile: isNewFile,
isEditingNewFile: isEditingNewFile,
databaseFile: databaseFile,
defaultFileConstants: {
defaultFileSizeInMb: defaultFileSizeInMb,
defaultFileGrowthInMb: defaultFileGrowthInMb,
defaultFileGrowthInPercent: defaultFileGrowthInPercent,
defaultMaxFileSizeLimitedToInMb: defaultMaxFileSizeLimitedToInMb
}
});
await dialog.open();
return await dialog.waitForClose();
}
//#endregion
//#region Database Properties - Options Tab
private initializeOptionsGeneralSection(): void {
let containers: azdata.Component[] = [];