Add database settings tab (#24260)

This commit is contained in:
Barbara Valdez
2023-08-31 14:26:08 -07:00
committed by GitHub
parent 9c99d50ea2
commit c695c5f941
7 changed files with 167 additions and 10 deletions

View File

@@ -16,6 +16,7 @@ export class IconPathHelper {
public static delete: IconPath;
public static user: IconPath;
public static group: IconPath;
public static folder: IconPath;
public static setExtensionContext(extensionContext: vscode.ExtensionContext) {
IconPathHelper.extensionContext = extensionContext;
@@ -31,5 +32,9 @@ export class IconPathHelper {
dark: IconPathHelper.extensionContext.asAbsolutePath('resources/dark/group_inverse.svg'),
light: IconPathHelper.extensionContext.asAbsolutePath('resources/light/group.svg')
};
IconPathHelper.folder = {
dark: IconPathHelper.extensionContext.asAbsolutePath('resources/folder.svg'),
light: IconPathHelper.extensionContext.asAbsolutePath('resources/folder.svg')
};
}
}

View File

@@ -32,6 +32,7 @@ export const ViewGeneralServerPropertiesDocUrl = 'https://learn.microsoft.com/sq
export const ViewMemoryServerPropertiesDocUrl = 'https://learn.microsoft.com/sql/database-engine/configure-windows/server-properties-memory-page';
export const ViewProcessorsServerPropertiesDocUrl = 'https://learn.microsoft.com/sql/database-engine/configure-windows/server-properties-processors-page';
export const ViewSecurityServerPropertiesDocUrl = 'https://learn.microsoft.com/sql/database-engine/configure-windows/server-properties-security-page';
export const ViewDatabaseSettingsPropertiesDocUrl = 'https://learn.microsoft.com/sql/database-engine/configure-windows/server-properties-database-settings-page';
export const DetachDatabaseDocUrl = 'https://go.microsoft.com/fwlink/?linkid=2240322';
export const AttachDatabaseDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/attach-a-database#to-attach-a-database';
export const DatabaseGeneralPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-general-page';

View File

@@ -541,6 +541,11 @@ export interface Server extends ObjectManagement.SqlObject {
numaNodes: NumaNode[];
authenticationMode: ServerLoginMode;
loginAuditing: AuditLevel;
checkCompressBackup: boolean;
checkBackupChecksum: boolean;
dataLocation: string;
logLocation: string;
backupLocation: string;
}
/**

View File

@@ -51,6 +51,8 @@ export const AddFileAriaLabel = localize('objectManagement.addFileText', "Add da
export const RemoveFileAriaLabel = localize('objectManagement.removeFileText', "Remove database file");
export const CreateObjectLabel = localize('objectManagement.createObjectLabel', "Create");
export const ApplyUpdatesLabel = localize('objectManagement.applyUpdatesLabel', "Apply");
export const allFiles = localize('objectManagement.allFiles', "All Files");
export const labelSelectFolder = localize('objectManagement.labelSelectFolder', "Select Folder");
export const DataFileLabel = localize('objectManagement.dataFileLabel', "Data");
export const LogFileLabel = localize('objectManagement.logFileLabel', "Log");
@@ -318,6 +320,14 @@ export const failedLoginsOnlyText = localize('objectManagement.failedLoginsOnlyT
export const successfulLoginsOnlyText = localize('objectManagement.successfulLoginsOnlyText', "Successful logins only");
export const bothFailedAndSuccessfulLoginsText = localize('objectManagement.bothFailedAndSuccessfulLoginsText', "Both failed and successful logins");
export const needToRestartServer = localize('objectManagement.needToRestartServer', "Changes require server restart in order to be effective");
export const logLocationText = localize('objectManagement.logLocationText', "Log");
export const dataLocationText = localize('objectManagement.dataLocationText', "Data");
export const backupLocationText = localize('objectManagement.backupLocationText', "Backup");
export const defaultLocationsLabel = localize('objectManagement.defaultLocationsLabel', "Database default locations");
export const databaseSettingsText = localize('objectManagement.databaseSettings', "Database Settings");
export const compressBackupText = localize('objectManagement.compressBackupText', "Compress Backup");
export const backupChecksumText = localize('objectManagement.backupChecksumText', "Backup checksum");
export const backupAndRestoreText = localize('objectManagement.backupAndRestoreText', "Backup and Restore");
//Database properties Dialog
export const LastDatabaseBackupText = localize('objectManagement.lastDatabaseBackup', "Last Database Backup");

View File

@@ -8,7 +8,7 @@ import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './obj
import { DefaultColumnCheckboxWidth } from '../../ui/dialogBase';
import { IObjectManagementService } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { ViewGeneralServerPropertiesDocUrl, ViewMemoryServerPropertiesDocUrl, ViewProcessorsServerPropertiesDocUrl, ViewSecurityServerPropertiesDocUrl } from '../constants';
import * as constants from '../constants';
import { Server, ServerViewInfo, NumaNode, AffinityType, ServerLoginMode, AuditLevel } from '../interfaces';
export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, ServerViewInfo> {
@@ -60,6 +60,15 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, S
private successfulLoginsOnlyRadioButton: azdata.RadioButtonComponent;
private bothFailedAndSuccessfulLoginsRadioButton: azdata.RadioButtonComponent;
private databaseSettingsTab: azdata.Tab;
private readonly databaseSettingsTabId: string = 'databaseSettingsId';
private databaseSettingsSection: azdata.GroupContainer;
private compressBackupCheckbox: azdata.CheckBoxComponent;
private backupChecksumCheckbox: azdata.CheckBoxComponent;
private dataLocationInput: azdata.InputBoxComponent;
private logLocationInput: azdata.InputBoxComponent;
private backupLocationInput: azdata.InputBoxComponent;
private activeTabId: string;
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
@@ -70,14 +79,20 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, S
let helpUrl = '';
switch (this.activeTabId) {
case this.generalTabId:
helpUrl = ViewGeneralServerPropertiesDocUrl;
helpUrl = constants.ViewGeneralServerPropertiesDocUrl;
break;
case this.memoryTabId:
helpUrl = ViewMemoryServerPropertiesDocUrl;
helpUrl = constants.ViewMemoryServerPropertiesDocUrl;
break;
case this.processorsTabId:
helpUrl = ViewProcessorsServerPropertiesDocUrl;
helpUrl = constants.ViewProcessorsServerPropertiesDocUrl;
break;
case this.securityTabId:
helpUrl = ViewSecurityServerPropertiesDocUrl;
helpUrl = constants.ViewSecurityServerPropertiesDocUrl;
break;
case this.databaseSettingsTabId:
helpUrl = constants.ViewDatabaseSettingsPropertiesDocUrl;
break;
default:
break;
}
@@ -91,7 +106,8 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, S
this.initializeMemorySection();
this.initializeProcessorsSection();
this.initializeSecuritySection();
const serverPropertiesTabGroup = { title: '', tabs: [this.generalTab, this.memoryTab, this.processorsTab, this.securityTab] };
this.initializeDatabaseSettingsSection();
const serverPropertiesTabGroup = { title: '', tabs: [this.generalTab, this.memoryTab, this.processorsTab, this.securityTab, this.databaseSettingsTab] };
const serverPropertiesTabbedPannel = this.modelView.modelBuilder.tabbedPanel()
.withTabs([serverPropertiesTabGroup])
.withProps({
@@ -499,4 +515,96 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase<Server, S
this.objectInfo.loginAuditing = AuditLevel.All;
}
}
private initializeDatabaseSettingsSection(): void {
const isEnabled = this.engineEdition !== azdata.DatabaseEngineEdition.SqlManagedInstance;
const dataLocationInputboxProps: azdata.InputBoxProperties = {
ariaLabel: localizedConstants.dataLocationText,
enabled: isEnabled,
value: this.objectInfo.dataLocation,
required: true
};
const logLocationInputboxProps: azdata.InputBoxProperties = {
ariaLabel: localizedConstants.logLocationText,
enabled: isEnabled,
value: this.objectInfo.logLocation,
required: true
};
const backupLocationInputboxProps: azdata.InputBoxProperties = {
ariaLabel: localizedConstants.backupLocationText,
enabled: isEnabled,
value: this.objectInfo.backupLocation,
required: true
};
this.compressBackupCheckbox = this.createCheckbox(localizedConstants.compressBackupText, async (newValue) => {
this.objectInfo.checkCompressBackup = newValue;
}, this.objectInfo.checkCompressBackup);
this.backupChecksumCheckbox = this.createCheckbox(localizedConstants.backupChecksumText, async (newValue) => {
this.objectInfo.checkBackupChecksum = newValue;
}, this.objectInfo.checkBackupChecksum);
const checkBoxContainer = this.createGroup(localizedConstants.backupAndRestoreText, [this.compressBackupCheckbox, this.backupChecksumCheckbox], false);
this.dataLocationInput = this.createInputBox(async (newValue) => {
this.objectInfo.dataLocation = newValue;
}, dataLocationInputboxProps);
const dataLocationButton = this.createBrowseButton(async () => {
const newPath = await this.selectFolder(this.objectInfo.dataLocation);
this.dataLocationInput.value = newPath;
this.objectInfo.dataLocation = newPath;
}, isEnabled);
const dataLocationInputContainer = this.createLabelInputContainer(localizedConstants.dataLocationText, [this.dataLocationInput, dataLocationButton])
this.logLocationInput = this.createInputBox(async (newValue) => {
this.objectInfo.logLocation = newValue;
}, logLocationInputboxProps);
const logLocationButton = this.createBrowseButton(async () => {
const newPath = await this.selectFolder(this.objectInfo.logLocation);
this.logLocationInput.value = newPath;
this.objectInfo.logLocation = newPath;
}, isEnabled);
const logLocationInputContainer = this.createLabelInputContainer(localizedConstants.logLocationText, [this.logLocationInput, logLocationButton])
this.backupLocationInput = this.createInputBox(async (newValue) => {
this.objectInfo.backupLocation = newValue;
}, backupLocationInputboxProps);
const backupLocationButton = this.createBrowseButton(async () => {
const newPath = await this.selectFolder(this.objectInfo.backupLocation);
this.backupLocationInput.value = newPath;
this.objectInfo.backupLocation = newPath;
}, isEnabled);
const backupLocationInputContainer = this.createLabelInputContainer(localizedConstants.backupLocationText, [this.backupLocationInput, backupLocationButton])
const defaultLocationsContainer = this.createGroup(localizedConstants.defaultLocationsLabel, [
dataLocationInputContainer,
logLocationInputContainer,
backupLocationInputContainer
], false);
this.databaseSettingsSection = this.createGroup('', [
checkBoxContainer,
defaultLocationsContainer
], false);
this.databaseSettingsTab = this.createTab(this.databaseSettingsTabId, localizedConstants.databaseSettingsText, this.databaseSettingsSection);
}
public async selectFolder(location: string): Promise<string | undefined> {
const allFilesFilter = localizedConstants.allFiles;
let filter: any = {};
filter[allFilesFilter] = '*';
let uris = await vscode.window.showOpenDialog({
filters: filter,
canSelectFiles: false,
canSelectMany: false,
canSelectFolders: true,
defaultUri: vscode.Uri.file(location),
openLabel: localizedConstants.labelSelectFolder
});
if (uris && uris.length > 0) {
return uris[0].fsPath;
}
return undefined;
}
}

View File

@@ -7,6 +7,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { EOL } from 'os';
import * as uiLoc from '../ui/localizedConstants';
import { IconPathHelper } from '../iconHelper';
export const DefaultLabelWidth = 150;
export const DefaultInputWidth = 300;
@@ -130,10 +131,16 @@ export abstract class DialogBase<DialogResult> {
return errors.length === 0;
}
protected createLabelInputContainer(label: string, component: azdata.Component, required: boolean = false): azdata.FlexContainer {
const labelComponent = this.modelView.modelBuilder.text().withProps({ width: DefaultLabelWidth, value: label, requiredIndicator: required, CSSStyles: { 'padding-right': '10px' } }).component();
const container = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'horizontal', flexWrap: 'nowrap', alignItems: 'center' }).withItems([labelComponent], { flex: '0 0 auto' }).component();
container.addItem(component, { flex: '1 1 auto' });
protected createLabelInputContainer(label: string, component: azdata.Component | azdata.Component[], required: boolean = false): azdata.FlexContainer {
let container: azdata.FlexContainer = undefined;
if (Array.isArray(component)) {
const labelComponent = this.modelView.modelBuilder.text().withProps({ width: DefaultLabelWidth - 40, value: label, requiredIndicator: required, CSSStyles: { 'padding-right': '10px' } }).component();
container = this.modelView.modelBuilder.flexContainer().withItems([labelComponent, ...component], { CSSStyles: { 'margin-right': '5px', 'margin-bottom': '10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
} else {
const labelComponent = this.modelView.modelBuilder.text().withProps({ width: DefaultLabelWidth, value: label, requiredIndicator: required, CSSStyles: { 'padding-right': '10px' } }).component();
container = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'horizontal', flexWrap: 'nowrap', alignItems: 'center' }).withItems([labelComponent], { flex: '0 0 auto' }).component();
container.addItem(component, { flex: '1 1 auto' });
}
return container;
}
@@ -371,6 +378,24 @@ export abstract class DialogBase<DialogResult> {
}).withItems(items, { flex: '0 0 auto' }).component();
}
protected createHorizontalContainer(header: string, items: azdata.Component[]): azdata.FlexContainer {
return this.modelView.modelBuilder.flexContainer().withItems(items, { CSSStyles: { 'margin-right': '5px', 'margin-bottom': '10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
}
protected createBrowseButton(handler: () => Promise<void>, enabled: boolean = true): azdata.ButtonComponent {
const button = this.dialogObject.modelView.modelBuilder.button().withProps({
ariaLabel: 'browse',
iconPath: IconPathHelper.folder,
width: '18px',
height: '20px',
enabled: enabled
}).component();
this.disposables.push(button.onDidClick(async () => {
await handler();
}));
return button;
}
protected createRadioButton(label: string, groupName: string, checked: boolean, handler: (checked: boolean) => Promise<void>, enabled: boolean = true): azdata.RadioButtonComponent {
const radio = this.modelView.modelBuilder.radioButton().withProps({
label: label,