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

@@ -0,0 +1,3 @@
<svg width="17" height="12" viewBox="0 0 17 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.8457 3V11C16.8457 11.1406 16.8197 11.2708 16.7676 11.3906C16.7155 11.5104 16.6426 11.6172 16.5488 11.7109C16.4603 11.7995 16.3561 11.8698 16.2363 11.9219C16.1165 11.974 15.9863 12 15.8457 12H1.8457C1.70508 12 1.57487 11.974 1.45508 11.9219C1.33529 11.8698 1.22852 11.7995 1.13477 11.7109C1.04622 11.6172 0.975911 11.5104 0.923828 11.3906C0.871745 11.2708 0.845703 11.1406 0.845703 11V1C0.845703 0.859375 0.871745 0.729167 0.923828 0.609375C0.975911 0.489583 1.04622 0.385417 1.13477 0.296875C1.22852 0.203125 1.33529 0.130208 1.45508 0.078125C1.57487 0.0260417 1.70508 0 1.8457 0H7.5957C7.78841 0 7.9681 0.0364583 8.13477 0.109375C8.30143 0.177083 8.45247 0.270833 8.58789 0.390625C8.72852 0.505208 8.85352 0.638021 8.96289 0.789062C9.07747 0.934896 9.18164 1.08594 9.27539 1.24219C9.3431 1.36198 9.4082 1.46875 9.4707 1.5625C9.53841 1.65625 9.61133 1.73698 9.68945 1.80469C9.77279 1.86719 9.86393 1.91667 9.96289 1.95312C10.0671 1.98438 10.1947 2 10.3457 2H15.8457C15.9863 2 16.1165 2.02604 16.2363 2.07812C16.3561 2.13021 16.4603 2.20312 16.5488 2.29688C16.6426 2.38542 16.7155 2.48958 16.7676 2.60938C16.8197 2.72917 16.8457 2.85938 16.8457 3ZM7.5957 1H1.8457V3H7.5957C7.73633 3 7.85352 2.97656 7.94727 2.92969C8.04622 2.88281 8.13737 2.82552 8.2207 2.75781C8.30924 2.6901 8.39779 2.61719 8.48633 2.53906C8.57487 2.45573 8.67643 2.38281 8.79102 2.32031C8.71289 2.23177 8.62956 2.11458 8.54102 1.96875C8.45768 1.81771 8.36654 1.67188 8.26758 1.53125C8.16862 1.38542 8.06185 1.26042 7.94727 1.15625C7.83789 1.05208 7.7207 1 7.5957 1ZM15.8457 11V3H10.3457C10.054 3 9.81706 3.02604 9.63477 3.07812C9.45768 3.125 9.30664 3.1849 9.18164 3.25781C9.06185 3.33073 8.95768 3.41146 8.86914 3.5C8.7806 3.58854 8.68164 3.66927 8.57227 3.74219C8.4681 3.8151 8.34049 3.8776 8.18945 3.92969C8.03841 3.97656 7.84049 4 7.5957 4H1.8457V11H15.8457Z" fill="#0078D4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

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 {
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();
const container = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'horizontal', flexWrap: 'nowrap', alignItems: 'center' }).withItems([labelComponent], { flex: '0 0 auto' }).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,