SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.restore-dialog {
padding: 15px
}
.modal .restore-dialog .dialog-label.header {
font-size: 15px;
}
.modal .restore-dialog .new-section {
padding-bottom: 50px;
}
.modal .restore-dialog .sub-section {
padding-left: 15px;
padding-right: 15px;
}
.modal .restore-dialog .dialog-input-section {
display: flex;
padding-left: 15px;
padding-right: 15px;
padding-top: 10px;
padding-bottom: 10px;
}
.modal .restore-dialog .dialog-input-section .dialog-label {
flex: 0 0 100px;
align-self: center;
}
.modal .restore-dialog .dialog-input-section .dialog-input {
flex: 1 1 auto;
}
.modal .restore-dialog .file-browser {
width: 30px;
}
.modal .restore-dialog .file-browser .monaco-button {
height: 100%;
}
.modal .restore-dialog .restore-plan-section {
width: 100%;
}
.modal .restore-dialog .restore-list {
width: 100%;
height: 500px;
margin-bottom: 15px;
overflow: hidden;
box-sizing: border-box;
}
.modal .restore-dialog .restore-list .backup-table-content .restore-data {
white-space: nowrap;
padding: 5px;
}
.modal .restore-dialog .more-options-button {
width: 100%;
display: flex;
flex-direction: row-reverse;
}
.modal .restore-dialog .more-options-button a.monaco-button.monaco-text-button {
margin-right: 15px;
}

View File

@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import data = require('data');
export class MssqlRestoreInfo implements data.RestoreInfo {
options: { [name: string]: any };
taskExecutionMode: data.TaskExecutionMode;
public constructor() {
this.options = {};
}
public get sessionId(): string {
return this.options['sessionId'];
}
public set sessionId(value: string) {
this.options['sessionId'] = value;
}
public get backupFilePaths(): string {
return this.options['backupFilePaths'];
}
public set backupFilePaths(value: string) {
this.options['backupFilePaths'] = value;
}
public get targetDatabaseName(): string {
return this.options['targetDatabaseName'];
}
public set targetDatabaseName(value: string) {
this.options['targetDatabaseName'] = value;
}
public get sourceDatabaseName(): string {
return this.options['sourceDatabaseName'];
}
public set sourceDatabaseName(value: string) {
this.options['sourceDatabaseName'] = value;
}
public get relocateDbFiles(): boolean {
return this.options['relocateDbFiles'];
}
public set relocateDbFiles(value: boolean) {
this.options['relocateDbFiles'] = value;
}
public get dataFileFolder(): string {
return this.options['dataFileFolder'];
}
public set dataFileFolder(value: string) {
this.options['dataFileFolder'] = value;
}
public get logFileFolder(): string {
return this.options['logFileFolder'];
}
public set logFileFolder(value: string) {
this.options['logFileFolder'] = value;
}
public get selectedBackupSets(): string[] {
return this.options['selectedBackupSets'];
}
public set selectedBackupSets(value: string[]) {
this.options['selectedBackupSets'] = value;
}
public get readHeaderFromMedia(): boolean {
return this.options['readHeaderFromMedia'];
}
public set readHeaderFromMedia(value: boolean) {
this.options['readHeaderFromMedia'] = value;
}
public get overwriteTargetDatabase(): boolean {
return this.options['overwriteTargetDatabase'];
}
public set overwriteTargetDatabase(value: boolean) {
this.options['overwriteTargetDatabase'] = value;
}
}

View File

@@ -0,0 +1,815 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/restoreDialog';
import { Builder, $ } from 'vs/base/browser/builder';
import { Button } from 'vs/base/browser/ui/button/button';
import Event, { Emitter } from 'vs/base/common/event';
import { Widget } from 'vs/base/browser/ui/widget';
import { MessageType, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachButtonStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { localize } from 'vs/nls';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { InputBox, OnLoseFocusParams } from 'sql/base/browser/ui/inputBox/inputBox';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
import { CheckboxSelectColumn } from 'sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper';
import { Modal } from 'sql/base/browser/ui/modal/modal';
import { attachModalDialogStyler, attachTableStyler, attachInputBoxStyler, attachSelectBoxStyler, attachEditableDropdownStyler } from 'sql/common/theme/styler';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement';
import * as BackupConstants from 'sql/parts/disasterRecovery/backup/constants';
import { RestoreViewModel, RestoreOptionParam, SouceDatabaseNamesParam } from 'sql/parts/disasterRecovery/restore/restoreViewModel';
import * as FileValidationConstants from 'sql/parts/fileBrowser/common/fileValidationServiceConstants';
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel';
import * as DOM from 'vs/base/browser/dom';
import * as data from 'data';
interface FileListElement {
logicalFileName: string;
fileType: string;
originalFileName: string;
restoreAs: string;
}
export class RestoreDialog extends Modal {
public viewModel: RestoreViewModel;
private _scriptButton: Button;
private _restoreButton: Button;
private _closeButton: Button;
private _optionsMap: { [name: string]: Widget } = {};
private _restoreLabel: string;
private _restoreTitle: string;
private _databaseTitle: string;
private _backupFileTitle: string;
private _ownerUri: string;
private _databaseDropdown: Dropdown;
// General options
private _filePathInputBox: InputBox;
private _browseFileButton: Button;
private _destinationRestoreToInputBox: InputBox;
private _restoreFromSelectBox: SelectBox;
private _sourceDatabaseSelectBox: SelectBox;
private _panel: TabbedPanel;
private _generalTabId: PanelTabIdentifier;
// File option
private readonly _relocateDatabaseFilesOption = 'relocateDbFiles';
private readonly _relocatedDataFileFolderOption = 'dataFileFolder';
private readonly _relocatedLogFileFolderOption = 'logFileFolder';
// other options
private readonly _withReplaceDatabaseOption = 'replaceDatabase';
private readonly _withKeepReplicationOption = 'keepReplication';
private readonly _withRestrictedUserOption = 'setRestrictedUser';
private readonly _recoveryStateOption = 'recoveryState';
private readonly _standbyFileOption = 'standbyFile';
private readonly _takeTaillogBackupOption = 'backupTailLog';
private readonly _tailLogWithNoRecoveryOption = 'tailLogWithNoRecovery';
private readonly _tailLogBackupFileOption = 'tailLogBackupFile';
private readonly _closeExistingConnectionsOption = 'closeExistingConnections';
private _restoreFromBackupFileElement: HTMLElement;
private _fileListTable: Table<FileListElement>;
private _fileListData: TableDataView<FileListElement>;
private _restorePlanTable: Table<Slick.SlickData>;
private _restorePlanData: TableDataView<Slick.SlickData>;
private _restorePlanColumn;
private _onRestore = new Emitter<boolean>();
public onRestore: Event<boolean> = this._onRestore.event;
private _onValidate = new Emitter<boolean>();
public onValidate: Event<boolean> = this._onValidate.event;
private _onCancel = new Emitter<void>();
public onCancel: Event<void> = this._onCancel.event;
private _onCloseEvent = new Emitter<void>();
public onCloseEvent: Event<void> = this._onCloseEvent.event;
private _onDatabaseListFocused = new Emitter<void>();
public onDatabaseListFocused: Event<void> = this._onDatabaseListFocused.event;
constructor(
optionsMetadata: data.ServiceOption[],
@IPartService partService: IPartService,
@IThemeService private _themeService: IThemeService,
@IContextViewService private _contextViewService: IContextViewService,
@IBootstrapService private _bootstrapService: IBootstrapService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super(localize('RestoreDialogTitle', 'Restore database'), TelemetryKeys.Restore, partService, telemetryService, contextKeyService, { hasErrors: true, isWide: true, hasSpinner: true });
this._restoreTitle = localize('restoreTitle', 'Restore database');
this._databaseTitle = localize('database', 'Database');
this._backupFileTitle = localize('backupFile', 'Backup file');
this._restoreLabel = localize('restore', 'Restore');
// view model
this.viewModel = new RestoreViewModel(optionsMetadata);
this.viewModel.onSetLastBackupTaken((value) => this.updateLastBackupTaken(value));
this.viewModel.onSetfilePath((value) => this.updateFilePath(value));
this.viewModel.onSetSourceDatabaseNames((databaseNamesParam) => this.updateSourceDatabaseName(databaseNamesParam));
this.viewModel.onSetTargetDatabaseName((value) => this.updateTargetDatabaseName(value));
this.viewModel.onSetLastBackupTaken((value) => this.updateLastBackupTaken(value));
this.viewModel.onSetRestoreOption((optionParams) => this.updateRestoreOption(optionParams));
this.viewModel.onUpdateBackupSetsToRestore((backupSets) => this.updateBackupSetsToRestore(backupSets));
this.viewModel.onUpdateRestoreDatabaseFiles((files) => this.updateRestoreDatabaseFiles(files));
}
public render() {
super.render();
attachModalDialogStyler(this, this._themeService);
let cancelLabel = localize('cancel', 'Cancel');
this._scriptButton = this.addFooterButton(localize('script', 'Script'), () => this.restore(true));
this._restoreButton = this.addFooterButton(this._restoreLabel, () => this.restore(false));
this._closeButton = this.addFooterButton(cancelLabel, () => this.cancel());
this.registerListeners();
this._destinationRestoreToInputBox.disable();
}
protected renderBody(container: HTMLElement) {
let restoreFromElement;
$().div({ class: 'restore-from' }, (restoreFromContainer) => {
restoreFromElement = restoreFromContainer.getHTMLElement();
this.createLabelElement(restoreFromContainer, localize('source', 'Source'), true);
this._restoreFromSelectBox = this.createSelectBoxHelper(restoreFromContainer, localize('restoreFrom', 'Restore from'), [this._databaseTitle, this._backupFileTitle], this._databaseTitle);
});
$().div({ class: 'backup-file-path' }, (filePathContainer) => {
filePathContainer.hide();
this._restoreFromBackupFileElement = filePathContainer.getHTMLElement();
let errorMessage = localize('missingBackupFilePathError', 'Backup file path is required.');
let validationOptions: IInputOptions = {
validationOptions: {
validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: errorMessage }) : null
},
placeholder: localize('multipleBackupFilePath', 'Please enter one or more file paths separated by commas')
};
filePathContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(localize('backupFilePath', "Backup file path"));
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
this._filePathInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, validationOptions);
});
inputContainer.div({ class: 'file-browser' }, (inputCellContainer) => {
this._browseFileButton = new Button(inputCellContainer);
this._browseFileButton.label = '...';
});
});
});
let sourceDatabasesElement;
$().div({ class: 'source-database-list' }, (sourceDatabasesContainer) => {
sourceDatabasesElement = sourceDatabasesContainer.getHTMLElement();
this._sourceDatabaseSelectBox = this.createSelectBoxHelper(sourceDatabasesContainer, localize('database', 'Database'), [], '');
});
// Source section
let sourceElement: HTMLElement;
$().div({ class: 'source-section new-section' }, (sourceContainer) => {
sourceElement = sourceContainer.getHTMLElement();
sourceContainer.append(restoreFromElement);
sourceContainer.append(this._restoreFromBackupFileElement);
sourceContainer.append(sourceDatabasesElement);
});
// Destination section
let destinationElement: HTMLElement;
$().div({ class: 'destination-section new-section' }, (destinationContainer) => {
destinationElement = destinationContainer.getHTMLElement();
this.createLabelElement(destinationContainer, localize('destination', 'Destination'), true);
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(localize('targetDatabase', 'Target database'));
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
// Get the bootstrap params and perform the bootstrap
inputCellContainer.style('width', '100%');
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
{
strictSelection: false
}
);
this._databaseDropdown.onValueChange(s => {
this.databaseSelected(s);
});
this._databaseDropdown.onBlur(() => {
this.databaseSelected(this._databaseDropdown.value);
});
this._databaseDropdown.onFocus(() => {
this._onDatabaseListFocused.fire();
});
this._databaseDropdown.value = this.viewModel.targetDatabaseName;
attachEditableDropdownStyler(this._databaseDropdown, this._themeService);
});
});
this._destinationRestoreToInputBox = this.createInputBoxHelper(destinationContainer, localize('restoreTo', 'Restore to'));
});
// Restore plan section
let restorePlanElement: HTMLElement;
$().div({ class: 'restore-plan-section new-section' }, (restorePlanContainer) => {
restorePlanElement = restorePlanContainer.getHTMLElement();
this.createLabelElement(restorePlanContainer, localize('restorePlan', 'Restore plan'), true);
this.createLabelElement(restorePlanContainer, localize('backupSetsToRestore', 'Backup sets to restore'));
// Backup sets table
restorePlanContainer.div({ class: 'dialog-input-section restore-list' }, (labelContainer) => {
this._restorePlanData = new TableDataView<Slick.SlickData>();
this._restorePlanTable = new Table<Slick.SlickData>(labelContainer.getHTMLElement(), this._restorePlanData, this._restorePlanColumn, { enableColumnReorder: false });
this._restorePlanTable.setSelectionModel(new RowSelectionModel({ selectActiveRow: false }));
this._restorePlanTable.onSelectedRowsChanged((e, data) => this.backupFileCheckboxChanged(e, data));
});
});
// Content in general tab
let generalTab = $('.restore-dialog');
generalTab.append(sourceElement);
generalTab.append(destinationElement);
generalTab.append(restorePlanElement);
// Content in file tab
let fileContentElement: HTMLElement;
$().div({ class: 'restore-dialog' }, (builder) => {
fileContentElement = builder.getHTMLElement();
// Restore database file as section
builder.div({ class: 'new-section' }, (sectionContainer) => {
this.createLabelElement(sectionContainer, localize('restoreDatabaseFileAs', 'Restore database files as'), true);
this.createOptionControl(sectionContainer, this._relocateDatabaseFilesOption);
sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => {
this.createOptionControl(subSectionContainer, this._relocatedDataFileFolderOption);
this.createOptionControl(subSectionContainer, this._relocatedLogFileFolderOption);
});
});
// Restore database file details section
builder.div({ class: 'new-section' }, (sectionContainer) => {
this.createLabelElement(sectionContainer, localize('restoreDatabaseFileDetails', 'Restore database file details'), true);
// file list table
sectionContainer.div({ class: 'dialog-input-section restore-list' }, (fileNameContainer) => {
let logicalFileName = localize('logicalFileName', 'Logical file Name');
let fileType = localize('fileType', 'File type');
let originalFileName = localize('originalFileName', 'Original File Name');
let restoreAs = localize('restoreAs', 'Restore as');
var columns = [{
id: 'logicalFileName',
name: logicalFileName,
field: 'logicalFileName'
}, {
id: 'fileType',
name: fileType,
field: 'fileType'
}, {
id: 'originalFileName',
name: originalFileName,
field: 'originalFileName'
}, {
id: 'restoreAs',
name: restoreAs,
field: 'restoreAs'
}];
this._fileListData = new TableDataView<FileListElement>();
this._fileListTable = new Table<FileListElement>(fileNameContainer.getHTMLElement(), this._fileListData, columns, { enableColumnReorder: false });
this._fileListTable.setSelectionModel(new RowSelectionModel());
});
});
});
// Content in options tab
let optionsContentElement: HTMLElement;
$().div({ class: 'restore-dialog' }, (builder) => {
optionsContentElement = builder.getHTMLElement();
// Restore options section
builder.div({ class: 'new-section' }, (sectionContainer) => {
this.createLabelElement(sectionContainer, localize('restoreOptions', 'Restore options'), true);
this.createOptionControl(sectionContainer, this._withReplaceDatabaseOption);
this.createOptionControl(sectionContainer, this._withKeepReplicationOption);
this.createOptionControl(sectionContainer, this._withRestrictedUserOption);
this.createOptionControl(sectionContainer, this._recoveryStateOption);
sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => {
this.createOptionControl(subSectionContainer, this._standbyFileOption);
});
});
// Tail-Log backup section
builder.div({ class: 'new-section' }, (sectionContainer) => {
this.createLabelElement(sectionContainer, localize('taillogBackup', 'Tail-Log backup'), true);
this.createOptionControl(sectionContainer, this._takeTaillogBackupOption);
sectionContainer.div({ class: 'sub-section' }, (subSectionContainer) => {
this.createOptionControl(subSectionContainer, this._tailLogWithNoRecoveryOption);
this.createOptionControl(subSectionContainer, this._tailLogBackupFileOption);
});
});
// Server connections section
builder.div({ class: 'new-section' }, (sectionContainer) => {
this.createLabelElement(sectionContainer, localize('serverConnection', 'Server connections'), true);
this.createOptionControl(sectionContainer, this._closeExistingConnectionsOption);
});
});
this._panel = new TabbedPanel(container);
this._generalTabId = this._panel.pushTab({
identifier: 'general',
title: localize('generalTitle', 'General'),
view: {
render: c => {
generalTab.appendTo(c);
},
layout: () => { }
}
});
let fileTab = this._panel.pushTab({
identifier: 'fileContent',
title: localize('filesTitle', 'Files'),
view: {
layout: () => { },
render: c => {
c.appendChild(fileContentElement);
}
}
});
this._panel.pushTab({
identifier: 'options',
title: localize('optionsTitle', 'Options'),
view: {
layout: () => { },
render: c => {
c.appendChild(optionsContentElement);
}
}
});
this._panel.onTabChange(c => {
if (c === fileTab && this._fileListTable) {
this._fileListTable.resizeCanvas();
this._fileListTable.autosizeColumns();
}
if (c !== this._generalTabId) {
this._restoreFromSelectBox.hideMessage();
}
});
}
private databaseSelected(dbName: string): void {
if (this.viewModel.targetDatabaseName !== dbName) {
this.viewModel.targetDatabaseName = dbName;
this.validateRestore(false);
}
}
public set databaseListOptions(vals: string[]) {
this._databaseDropdown.values = vals;
}
private createLabelElement(container: Builder, content: string, isHeader?: boolean) {
let className = 'dialog-label';
if (isHeader) {
className += ' header';
}
container.div({ class: className }, (labelContainer) => {
labelContainer.innerHtml(content);
});
}
private createOptionControl(container: Builder, optionName: string): void {
let option = this.viewModel.getOptionMetadata(optionName);
let propertyWidget: any;
switch (option.valueType) {
case ServiceOptionType.boolean:
propertyWidget = this.createCheckBoxHelper(container, option.description,
DialogHelper.getBooleanValueFromStringOrBoolean(option.defaultValue), () => this.onBooleanOptionChecked(optionName));
this._register(attachCheckboxStyler(propertyWidget, this._themeService));
break;
case ServiceOptionType.category:
propertyWidget = this.createSelectBoxHelper(container, option.description, option.categoryValues.map(c => c.displayName), DialogHelper.getCategoryDisplayName(option.categoryValues, option.defaultValue));
this._register(attachSelectBoxStyler(propertyWidget, this._themeService));
this._register(propertyWidget.onDidSelect(selectedDatabase => {
this.onCatagoryOptionChanged(optionName);
}));
break;
case ServiceOptionType.string:
propertyWidget = this.createInputBoxHelper(container, option.description);
this._register(attachInputBoxStyler(propertyWidget, this._themeService));
this._register(propertyWidget.onLoseFocus(params => {
this.onStringOptionChanged(optionName, params);
}));
}
this._optionsMap[optionName] = propertyWidget;
}
private onBooleanOptionChecked(optionName: string) {
this.viewModel.setOptionValue(optionName, (<Checkbox>this._optionsMap[optionName]).checked);
this.validateRestore(false);
}
private onCatagoryOptionChanged(optionName: string) {
this.viewModel.setOptionValue(optionName, (<SelectBox>this._optionsMap[optionName]).value);
this.validateRestore(false);
}
private onStringOptionChanged(optionName: string, params: OnLoseFocusParams) {
if (params.hasChanged && params.value) {
this.viewModel.setOptionValue(optionName, params.value);
this.validateRestore(false);
}
}
private createCheckBoxHelper(container: Builder, label: string, isChecked: boolean, onCheck: (viaKeyboard: boolean) => void): Checkbox {
let checkbox: Checkbox;
container.div({ class: 'dialog-input-section' }, (inputCellContainer) => {
checkbox = DialogHelper.createCheckBox(inputCellContainer, label, 'restore-checkbox', isChecked, onCheck);
});
return checkbox;
}
private createSelectBoxHelper(container: Builder, label: string, options: string[], selectedOption: string): SelectBox {
let selectBox: SelectBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
selectBox = new SelectBox(options, selectedOption, inputCellContainer.getHTMLElement(), this._contextViewService);
selectBox.render(inputCellContainer.getHTMLElement());
});
});
return selectBox;
}
private createInputBoxHelper(container: Builder, label: string, options?: IInputOptions): InputBox {
let inputBox: InputBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, options);
});
});
return inputBox;
}
private resetRestoreContent(): void {
this._restorePlanData.clear();
this._fileListData.clear();
this._restoreButton.enabled = false;
this._scriptButton.enabled = false;
}
public onValidateResponseFail(errorMessage: string) {
this.resetRestoreContent();
if (this.isRestoreFromDatabaseSelected) {
this._sourceDatabaseSelectBox.showMessage({ type: MessageType.ERROR, content: errorMessage });
} else {
this._sourceDatabaseSelectBox.setOptions([]);
this._filePathInputBox.showMessage({ type: MessageType.ERROR, content: errorMessage });
}
}
public removeErrorMessage() {
this._filePathInputBox.hideMessage();
this._sourceDatabaseSelectBox.hideMessage();
this._destinationRestoreToInputBox.hideMessage();
}
public enableRestoreButton(enabled: boolean) {
this.hideSpinner();
this._restoreButton.enabled = enabled;
this._scriptButton.enabled = enabled;
}
public showError(errorMessage: string): void {
this.setError(errorMessage);
}
private backupFileCheckboxChanged(e: Slick.EventData, data: Slick.OnSelectedRowsChangedEventArgs<Slick.SlickData>): void {
let selectedFiles = [];
data.grid.getSelectedRows().forEach(row => {
selectedFiles.push(data.grid.getDataItem(row)['Id']);
});
let isSame = false;
if (this.viewModel.selectedBackupSets && this.viewModel.selectedBackupSets.length === selectedFiles.length) {
isSame = this.viewModel.selectedBackupSets.some(item => selectedFiles.includes(item));
}
if (!isSame) {
this.viewModel.selectedBackupSets = selectedFiles;
this.validateRestore(false);
}
}
private registerListeners(): void {
// Theme styler
this._register(attachInputBoxStyler(this._filePathInputBox, this._themeService));
this._register(attachInputBoxStyler(this._destinationRestoreToInputBox, this._themeService));
this._register(attachSelectBoxStyler(this._restoreFromSelectBox, this._themeService));
this._register(attachSelectBoxStyler(this._sourceDatabaseSelectBox, this._themeService));
this._register(attachButtonStyler(this._browseFileButton, this._themeService));
this._register(attachButtonStyler(this._scriptButton, this._themeService));
this._register(attachButtonStyler(this._restoreButton, this._themeService));
this._register(attachButtonStyler(this._closeButton, this._themeService));
this._register(attachTableStyler(this._fileListTable, this._themeService));
this._register(attachTableStyler(this._restorePlanTable, this._themeService));
this._register(this._filePathInputBox.onLoseFocus(params => {
this.onFilePathLoseFocus(params);
}));
this._register(DOM.addDisposableListener(this._browseFileButton.getElement(), DOM.EventType.CLICK, () => {
if (this._browseFileButton.enabled) {
this.onFileBrowserRequested();
}
}));
this._register(this._sourceDatabaseSelectBox.onDidSelect(selectedDatabase => {
this.onSourceDatabaseChanged(selectedDatabase.selected);
}));
this._register(this._restoreFromSelectBox.onDidSelect(selectedRestoreFrom => {
this.onRestoreFromChanged(selectedRestoreFrom.selected);
}));
}
private onFileBrowserRequested(): void {
this._bootstrapService.fileBrowserDialogService.showDialog(this._ownerUri,
this.viewModel.defaultBackupFolder,
BackupConstants.fileFiltersSet,
FileValidationConstants.restore,
true,
filepath => this.onFileBrowsed(filepath));
}
private onFileBrowsed(filepath: string) {
var oldFilePath = this._filePathInputBox.value;
if (DialogHelper.isNullOrWhiteSpace(this._filePathInputBox.value)) {
this._filePathInputBox.value = filepath;
} else {
this._filePathInputBox.value = this._filePathInputBox.value + ', ' + filepath;
}
if (oldFilePath !== this._filePathInputBox.value) {
this.onFilePathChanged(this._filePathInputBox.value);
}
}
private onFilePathLoseFocus(params: OnLoseFocusParams) {
if (params.value) {
if (params.hasChanged || (this.viewModel.filePath !== params.value)) {
this.onFilePathChanged(params.value);
}
}
}
private onFilePathChanged(filePath: string) {
this.viewModel.filePath = filePath;
this.viewModel.selectedBackupSets = null;
this.validateRestore(true);
}
private onSourceDatabaseChanged(selectedDatabase: string) {
this.viewModel.sourceDatabaseName = selectedDatabase;
this.viewModel.selectedBackupSets = null;
this.viewModel.resetTailLogBackupFile();
this.validateRestore(true);
}
private onRestoreFromChanged(selectedRestoreFrom: string) {
this.removeErrorMessage();
if (selectedRestoreFrom === this._backupFileTitle) {
this.viewModel.onRestoreFromChanged(true);
new Builder(this._restoreFromBackupFileElement).show();
} else {
this.viewModel.onRestoreFromChanged(false);
new Builder(this._restoreFromBackupFileElement).hide();
}
this.resetRestoreContent();
}
private get isRestoreFromDatabaseSelected(): boolean {
return this._restoreFromSelectBox.value === this._databaseTitle;
}
public validateRestore(overwriteTargetDatabase: boolean = false): void {
this.showSpinner();
this._restoreButton.enabled = false;
this._scriptButton.enabled = false;
this._onValidate.fire(overwriteTargetDatabase);
}
public restore(isScriptOnly: boolean): void {
if (this._restoreButton.enabled) {
this._onRestore.fire(isScriptOnly);
}
}
public hideError() {
this.setError('');
}
/* Overwrite esapce key behavior */
protected onClose() {
this.cancel();
}
/* Overwrite enter key behavior */
protected onAccept() {
this.restore(false);
}
public cancel() {
this._onCancel.fire();
this.close();
}
public close() {
this.resetDialog();
this.hide();
this._onCloseEvent.fire();
}
private resetDialog(): void {
this.hideError();
this._restoreFromSelectBox.selectWithOptionName(this._databaseTitle);
this.onRestoreFromChanged(this._databaseTitle);
this._sourceDatabaseSelectBox.select(0);
this._panel.showTab(this._generalTabId);
this.removeErrorMessage();
this.resetRestoreContent();
}
public open(serverName: string, ownerUri: string) {
this.title = this._restoreTitle + ' - ' + serverName;
this._ownerUri = ownerUri;
this.show();
this._filePathInputBox.focus();
}
protected layout(height?: number): void {
// Nothing currently laid out statically in this class
}
public dispose(): void {
super.dispose();
for (var key in this._optionsMap) {
var widget: Widget = this._optionsMap[key];
widget.dispose();
delete this._optionsMap[key];
}
}
private updateLastBackupTaken(value: string) {
this._destinationRestoreToInputBox.value = value;
}
private updateFilePath(value: string) {
this._filePathInputBox.value = value;
if (!value) {
this._filePathInputBox.hideMessage();
}
}
private updateSourceDatabaseName(databaseNamesParam: SouceDatabaseNamesParam) {
// Always adding an empty name as the first item so if the selected db name is not in the list,
// The empty string would be selected and not the first db in the list
let dbNames: string[] = [];
if (this.isRestoreFromDatabaseSelected && databaseNamesParam.databaseNames
&& databaseNamesParam.databaseNames.length > 0 && databaseNamesParam.databaseNames[0] !== '') {
dbNames = [''].concat(databaseNamesParam.databaseNames);
} else {
dbNames = databaseNamesParam.databaseNames;
}
this._sourceDatabaseSelectBox.setOptions(dbNames);
this._sourceDatabaseSelectBox.selectWithOptionName(databaseNamesParam.selectedDatabase);
}
private updateTargetDatabaseName(value: string) {
this._databaseDropdown.value = value;
}
private updateRestoreOption(optionParam: RestoreOptionParam) {
let widget = this._optionsMap[optionParam.optionName];
if (widget) {
if (widget instanceof Checkbox) {
(<Checkbox>widget).checked = optionParam.value;
this.enableDisableWiget(widget, optionParam.isReadOnly);
} else if (widget instanceof SelectBox) {
(<SelectBox>widget).selectWithOptionName(optionParam.value);
this.enableDisableWiget(widget, optionParam.isReadOnly);
} else if (widget instanceof InputBox) {
(<InputBox>widget).value = optionParam.value;
this.enableDisableWiget(widget, optionParam.isReadOnly);
}
}
}
private enableDisableWiget(widget: Checkbox | SelectBox | InputBox, isReadOnly: boolean) {
if (isReadOnly) {
widget.disable();
} else {
widget.enable();
}
}
private updateRestoreDatabaseFiles(dbFiles: data.RestoreDatabaseFileInfo[]) {
this._fileListData.clear();
if (dbFiles) {
let data = [];
for (let i = 0; i < dbFiles.length; i++) {
data[i] = {
logicalFileName: dbFiles[i].logicalFileName,
fileType: dbFiles[i].fileType,
originalFileName: dbFiles[i].originalFileName,
restoreAs: dbFiles[i].restoreAsFileName
};
}
this._fileListData.push(data);
}
}
private updateBackupSetsToRestore(backupSetsToRestore: data.DatabaseFileInfo[]) {
this._restorePlanData.clear();
if (backupSetsToRestore && backupSetsToRestore.length > 0) {
if (!this._restorePlanColumn) {
let firstRow = backupSetsToRestore[0];
this._restorePlanColumn = firstRow.properties.map(item => {
return {
id: item.propertyName,
name: item.propertyDisplayName,
field: item.propertyName
};
});
let checkboxSelectColumn = new CheckboxSelectColumn({ title: this._restoreLabel });
this._register(attachCheckboxStyler(checkboxSelectColumn, this._themeService));
this._restorePlanColumn.unshift(checkboxSelectColumn.getColumnDefinition());
this._restorePlanTable.columns = this._restorePlanColumn;
this._restorePlanTable.registerPlugin(checkboxSelectColumn);
this._restorePlanTable.autosizeColumns();
}
let data = [];
let selectedRow = [];
for (let i = 0; i < backupSetsToRestore.length; i++) {
let backupFile = backupSetsToRestore[i];
let newData = {};
for (let j = 0; j < backupFile.properties.length; j++) {
newData[backupFile.properties[j].propertyName] = backupFile.properties[j].propertyValueDisplayName;
}
data.push(newData);
if (backupFile.isSelected) {
selectedRow.push(i);
}
}
this._restorePlanData.push(data);
this._restorePlanTable.setSelectedRows(selectedRow);
}
}
}

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as types from 'vs/base/common/types';
import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import * as ConnectionConstants from 'sql/parts/connection/common/constants';
import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo';
import { IDisasterRecoveryService, IRestoreDialogController, TaskExecutionMode } from 'sql/parts/disasterRecovery/common/interfaces';
import { MssqlRestoreInfo } from 'sql/parts/disasterRecovery/restore/mssqlRestoreInfo';
import { RestoreDialog } from 'sql/parts/disasterRecovery/restore/restoreDialog';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as Utils from 'sql/parts/connection/common/utils';
import * as data from 'data';
export class RestoreDialogController implements IRestoreDialogController {
_serviceBrand: any;
private _restoreDialogs: { [provider: string]: RestoreDialog | OptionsDialog } = {};
private _currentProvider: string;
private _ownerUri: string;
private _sessionId: string;
private readonly _restoreFeature = 'Restore';
private _optionValues: { [optionName: string]: any } = {};
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IDisasterRecoveryService private _disasterRecoveryService: IDisasterRecoveryService,
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService
) {
}
private handleOnRestore(isScriptOnly: boolean = false): void {
let restoreOption = this.setRestoreOption();
if (isScriptOnly) {
restoreOption.taskExecutionMode = TaskExecutionMode.script;
} else {
restoreOption.taskExecutionMode = TaskExecutionMode.executeAndScript;
}
this._disasterRecoveryService.restore(this._ownerUri, restoreOption);
let restoreDialog = this._restoreDialogs[this._currentProvider];
restoreDialog.close();
}
private handleMssqlOnValidateFile(overwriteTargetDatabase: boolean = false): void {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
this._disasterRecoveryService.getRestorePlan(this._ownerUri, this.setRestoreOption(overwriteTargetDatabase)).then(restorePlanResponse => {
this._sessionId = restorePlanResponse.sessionId;
if (restorePlanResponse.errorMessage) {
restoreDialog.onValidateResponseFail(restorePlanResponse.errorMessage);
} else {
restoreDialog.removeErrorMessage();
restoreDialog.viewModel.onRestorePlanResponse(restorePlanResponse);
}
if (restorePlanResponse.canRestore && !this.isEmptyBackupset()) {
restoreDialog.enableRestoreButton(true);
} else {
restoreDialog.enableRestoreButton(false);
}
}, error => {
restoreDialog.showError(error);
});
}
/**
* Temporary fix for bug #2506: Restore button not disabled when there's not backup set to restore
* Will remove this function once there is a fix in the service (bug #2572)
*/
private isEmptyBackupset(): boolean {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
if (!types.isUndefinedOrNull(restoreDialog.viewModel.selectedBackupSets) && restoreDialog.viewModel.selectedBackupSets.length === 0) {
return true;
}
return false;
}
private getMssqlRestoreConfigInfo(): Promise<void> {
return new Promise<void>((resolve, reject) => {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
this._disasterRecoveryService.getRestoreConfigInfo(this._ownerUri).then(restoreConfigInfo => {
restoreDialog.viewModel.updateOptionWithConfigInfo(restoreConfigInfo.configInfo);
resolve();
}, error => {
restoreDialog.showError(error);
reject(error);
});
});
}
private setRestoreOption(overwriteTargetDatabase: boolean = false): data.RestoreInfo {
let restoreInfo = undefined;
let providerId: string = this.getCurrentProviderId();
if (providerId === ConnectionConstants.mssqlProviderName) {
restoreInfo = new MssqlRestoreInfo();
if (this._sessionId) {
restoreInfo.sessionId = this._sessionId;
}
let restoreDialog = this._restoreDialogs[providerId] as RestoreDialog;
restoreInfo.backupFilePaths = restoreDialog.viewModel.filePath;
// todo: Need to change restoreInfo.readHeaderFromMedia when implement restore from database
restoreInfo.readHeaderFromMedia = restoreDialog.viewModel.readHeaderFromMedia;
restoreInfo.selectedBackupSets = restoreDialog.viewModel.selectedBackupSets;
if (restoreDialog.viewModel.sourceDatabaseName) {
restoreInfo.sourceDatabaseName = restoreDialog.viewModel.sourceDatabaseName;
}
if (restoreDialog.viewModel.targetDatabaseName) {
restoreInfo.targetDatabaseName = restoreDialog.viewModel.targetDatabaseName;
}
restoreInfo.overwriteTargetDatabase = overwriteTargetDatabase;
// Set other restore options
restoreDialog.viewModel.getRestoreAdvancedOptions(restoreInfo.options);
} else {
restoreInfo = { options: this._optionValues };
}
return restoreInfo;
}
private getRestoreOption(): data.ServiceOption[] {
let options: data.ServiceOption[] = [];
let providerId: string = this.getCurrentProviderId();
let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId);
if (providerCapabilities) {
let restoreMetadataProvider = providerCapabilities.features.find(f => f.featureName === this._restoreFeature);
if (restoreMetadataProvider) {
options = restoreMetadataProvider.optionsMetadata;
}
}
return options;
}
private handleOnClose(): void {
this._connectionService.disconnect(this._ownerUri);
}
private handleOnCancel(): void {
let restoreInfo = new MssqlRestoreInfo();
restoreInfo.sessionId = this._sessionId;
this._disasterRecoveryService.cancelRestorePlan(this._ownerUri, restoreInfo).then(() => {
this._connectionService.disconnect(this._ownerUri);
});
}
public showDialog(connection: IConnectionProfile): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
let result: void;
this._ownerUri = this._connectionService.getConnectionId(connection)
+ ProviderConnectionInfo.idSeparator
+ Utils.ConnectionUriRestoreIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator
+ '0';
if (!this._connectionService.isConnected(this._ownerUri)) {
this._connectionService.connect(connection, this._ownerUri).then(connectionResult => {
this._sessionId = null;
this._currentProvider = this.getCurrentProviderId();
if (!this._restoreDialogs[this._currentProvider]) {
let newRestoreDialog: RestoreDialog | OptionsDialog = undefined;
if (this._currentProvider === ConnectionConstants.mssqlProviderName) {
let provider = this._currentProvider;
newRestoreDialog = this._instantiationService.createInstance(RestoreDialog, this.getRestoreOption());
newRestoreDialog.onCancel(() => this.handleOnCancel());
newRestoreDialog.onRestore((isScriptOnly) => this.handleOnRestore(isScriptOnly));
newRestoreDialog.onValidate((overwriteTargetDatabase) => this.handleMssqlOnValidateFile(overwriteTargetDatabase));
newRestoreDialog.onDatabaseListFocused(() => this.fetchDatabases(provider));
} else {
newRestoreDialog = this._instantiationService.createInstance(
OptionsDialog, 'Restore database - ' + connection.serverName + ':' + connection.databaseName, 'RestoreOptions', undefined);
newRestoreDialog.onOk(() => this.handleOnRestore());
}
newRestoreDialog.onCloseEvent(() => this.handleOnClose());
newRestoreDialog.render();
this._restoreDialogs[this._currentProvider] = newRestoreDialog;
}
if (this._currentProvider === ConnectionConstants.mssqlProviderName) {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
restoreDialog.viewModel.resetRestoreOptions(connection.databaseName);
this.getMssqlRestoreConfigInfo().then(() => {
restoreDialog.open(connection.serverName, this._ownerUri);
restoreDialog.validateRestore();
}, restoreConfigError => {
reject(restoreConfigError);
});
} else {
let restoreDialog = this._restoreDialogs[this._currentProvider] as OptionsDialog;
restoreDialog.open(this.getRestoreOption(), this._optionValues);
}
resolve(result);
}, error => {
reject(error);
});
}
});
}
private getCurrentProviderId(): string {
return this._connectionService.getProviderIdFromUri(this._ownerUri);
}
private fetchDatabases(provider: string): void {
this._connectionService.listDatabases(this._ownerUri).then(result => {
if (result && result.databaseNames) {
(<RestoreDialog> this._restoreDialogs[provider]).databaseListOptions = result.databaseNames;
}
});
}
}

View File

@@ -0,0 +1,309 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as data from 'data';
import { ServiceOptionType } from 'sql/parts/connection/common/connectionManagement';
import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper';
import * as types from 'vs/base/common/types';
import Event, { Emitter } from 'vs/base/common/event';
export interface RestoreOptionsElement {
optionMetadata: data.ServiceOption;
defaultValue: any;
currentValue: any;
}
/**
* Parameters for setting the widget in the restore dialog
*/
export interface RestoreOptionParam {
optionName: string;
value: any;
isReadOnly: boolean;
}
/**
* Parameters for setting the list of source database names and selected database name in the restore dialog
*/
export interface SouceDatabaseNamesParam {
databaseNames: string[];
selectedDatabase: string;
}
/**
* View model for restore dialog
*/
export class RestoreViewModel {
public filePath: string;
public sourceDatabaseName: string;
public targetDatabaseName: string;
public lastBackupTaken: string;
public databaseList: string[];
public readHeaderFromMedia: boolean;
public selectedBackupSets: string[];
public defaultBackupFolder: string;
private _onSetLastBackupTaken = new Emitter<string>();
public onSetLastBackupTaken: Event<string> = this._onSetLastBackupTaken.event;
private _onSetfilePath = new Emitter<string>();
public onSetfilePath: Event<string> = this._onSetfilePath.event;
private _onSetSourceDatabaseNames = new Emitter<SouceDatabaseNamesParam>();
public onSetSourceDatabaseNames: Event<SouceDatabaseNamesParam> = this._onSetSourceDatabaseNames.event;
private _onSetTargetDatabaseName = new Emitter<string>();
public onSetTargetDatabaseName: Event<string> = this._onSetTargetDatabaseName.event;
private _onSetRestoreOption = new Emitter<RestoreOptionParam>();
public onSetRestoreOption: Event<RestoreOptionParam> = this._onSetRestoreOption.event;
private _onUpdateBackupSetsToRestore = new Emitter<data.DatabaseFileInfo[]>();
public onUpdateBackupSetsToRestore: Event<data.DatabaseFileInfo[]> = this._onUpdateBackupSetsToRestore.event;
private _onUpdateRestoreDatabaseFiles = new Emitter<data.RestoreDatabaseFileInfo[]>();
public onUpdateRestoreDatabaseFiles: Event<data.RestoreDatabaseFileInfo[]> = this._onUpdateRestoreDatabaseFiles.event;
private _optionsMap: { [name: string]: RestoreOptionsElement } = {};
constructor(optionsMetadata: data.ServiceOption[]) {
optionsMetadata.forEach(optionMetadata => {
let defaultValue = this.getDisplayValue(optionMetadata, optionMetadata.defaultValue);
this._optionsMap[optionMetadata.name] = {
optionMetadata: optionMetadata,
defaultValue: defaultValue,
currentValue: defaultValue
};
});
}
/**
* Get option display value
*/
public getDisplayValue(optionMetadata: data.ServiceOption, optionValue: any): any {
let displayValue: any;
switch (optionMetadata.valueType) {
case ServiceOptionType.boolean:
displayValue = DialogHelper.getBooleanValueFromStringOrBoolean(optionValue);
break;
case ServiceOptionType.category:
let optionName = optionValue;
if (!optionName && optionMetadata.categoryValues[0]) {
optionName = optionMetadata.categoryValues[0].name;
}
displayValue = DialogHelper.getCategoryDisplayName(optionMetadata.categoryValues, optionName);
break;
case ServiceOptionType.string:
displayValue = optionValue ? optionValue : '';
}
return displayValue;
}
/**
* On restore from changed set readHeaderFromMedia and reset the source database names and selected database name based on isFromBackupFile value.
*/
public onRestoreFromChanged(isFromBackupFile: boolean) {
this.readHeaderFromMedia = isFromBackupFile;
if (isFromBackupFile) {
this.updateFilePath('');
this.updateSourceDatabaseNames([], '');
} else {
this.updateSourceDatabaseNames(this.databaseList, this.databaseList[0]);
}
}
/**
* Get option metadata from the option map
*/
public getOptionMetadata(optionName: string): data.ServiceOption {
return this._optionsMap[optionName] ? this._optionsMap[optionName].optionMetadata : undefined;
}
/**
* Set current value for restore option
*/
public setOptionValue(optionName: string, value: any): void {
if (this._optionsMap[optionName]) {
this._optionsMap[optionName].currentValue = value;
}
}
/**
* Reset tail log backup file (temporary fix for bug#2838)
*/
public resetTailLogBackupFile(): void {
const tailLogBackupFileOption = 'tailLogBackupFile';
if (this._optionsMap[tailLogBackupFileOption]) {
this._optionsMap[tailLogBackupFileOption].currentValue = null;
this._optionsMap[tailLogBackupFileOption].defaultValue = null;
}
}
/**
* Get current value for restore option
*/
public getOptionValue(optionName: string): any {
if (this._optionsMap[optionName]) {
return this._optionsMap[optionName].currentValue;
}
return undefined;
}
/**
* Get restore advanced options. Only return the options that are different from the default options
*/
public getRestoreAdvancedOptions(options: { [name: string]: any }) {
for (let key in this._optionsMap) {
let optionElement = this._optionsMap[key];
switch (optionElement.optionMetadata.valueType) {
case ServiceOptionType.boolean:
if (optionElement.currentValue !== optionElement.defaultValue) {
options[key] = optionElement.currentValue;
}
break;
case ServiceOptionType.category:
if (optionElement.currentValue !== optionElement.defaultValue) {
options[key] = DialogHelper.getCategoryName(optionElement.optionMetadata.categoryValues, optionElement.currentValue);
}
break;
case ServiceOptionType.string:
if (optionElement.currentValue && optionElement.currentValue !== optionElement.defaultValue) {
options[key] = optionElement.currentValue;
}
}
}
}
/**
* On restore plan response will update all the information from restore plan response
*/
public onRestorePlanResponse(restorePlanResponse: data.RestorePlanResponse): void {
if (restorePlanResponse.planDetails && restorePlanResponse.planDetails['lastBackupTaken']) {
this.updateLastBackupTaken(restorePlanResponse.planDetails['lastBackupTaken'].currentValue);
}
if (restorePlanResponse.planDetails && restorePlanResponse.planDetails['targetDatabaseName']) {
this.updateTargetDatabaseName(restorePlanResponse.planDetails['targetDatabaseName'].currentValue);
}
this._onUpdateRestoreDatabaseFiles.fire(restorePlanResponse.dbFiles);
this.updateSourceDatabaseNames(restorePlanResponse.databaseNamesFromBackupSets, restorePlanResponse.planDetails['sourceDatabaseName'].currentValue);
this.updateOptionWithPlanDetail(restorePlanResponse.planDetails);
this.updateBackupSetsToRestore(restorePlanResponse.backupSetsToRestore);
}
/**
* Update options with plan details
*/
public updateOptionWithPlanDetail(planDetails: { [key: string]: data.RestorePlanDetailInfo }): void {
if (planDetails) {
for (var key in planDetails) {
let optionElement = this._optionsMap[key];
if (optionElement) {
let planDetailInfo = planDetails[key];
optionElement.defaultValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo.defaultValue);
optionElement.currentValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo.currentValue);
this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].currentValue, isReadOnly: planDetailInfo.isReadOnly });
}
}
}
}
/**
* Update options with restore config info. The option values will be both default and current values.
*/
public updateOptionWithConfigInfo(configInfo: { [key: string]: any }): void {
if (configInfo) {
if (configInfo['sourceDatabaseNamesWithBackupSets']) {
let databaseList = configInfo['sourceDatabaseNamesWithBackupSets'];
if (types.isStringArray(databaseList)) {
this.databaseList = databaseList;
this.databaseList.unshift('');
this.readHeaderFromMedia = false;
this.updateSourceDatabaseNames(this.databaseList, this.sourceDatabaseName);
}
}
if (configInfo['defaultBackupFolder']) {
this.defaultBackupFolder = configInfo['defaultBackupFolder'];
}
for (var key in configInfo) {
let optionElement = this._optionsMap[key];
if (optionElement) {
let planDetailInfo = configInfo[key];
optionElement.defaultValue = this.getDisplayValue(optionElement.optionMetadata, planDetailInfo);
optionElement.currentValue = optionElement.defaultValue;
this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].currentValue, isReadOnly: true });
}
}
}
}
/**
* Update backup sets to restore
*/
public updateBackupSetsToRestore(backupSetsToRestore: data.DatabaseFileInfo[]): void {
this.selectedBackupSets = null;
if (backupSetsToRestore) {
this.selectedBackupSets = [];
backupSetsToRestore.forEach(backupFile => {
if (backupFile.isSelected) {
this.selectedBackupSets.push(backupFile.id);
}
});
this._onUpdateBackupSetsToRestore.fire(backupSetsToRestore);
}
}
/**
* Reset restore options to the default value
*/
public resetRestoreOptions(databaseName: string): void {
this.updateTargetDatabaseName(databaseName);
this.updateSourceDatabaseNames([], databaseName);
this.updateFilePath('');
this.updateLastBackupTaken('');
this.databaseList = [];
this.selectedBackupSets = null;
for (var key in this._optionsMap) {
this._optionsMap[key].defaultValue = this.getDisplayValue(this._optionsMap[key].optionMetadata, this._optionsMap[key].optionMetadata.defaultValue);
this._optionsMap[key].currentValue = this._optionsMap[key].defaultValue;
this._onSetRestoreOption.fire({ optionName: key, value: this._optionsMap[key].defaultValue, isReadOnly: false });
}
}
/**
* Update last backup taken
*/
public updateLastBackupTaken(value: string) {
this.lastBackupTaken = value;
this._onSetLastBackupTaken.fire(value);
}
/**
* Update file path
*/
public updateFilePath(value: string) {
this.filePath = value;
this._onSetfilePath.fire(value);
}
/**
* Update source database names and selected database
*/
public updateSourceDatabaseNames(options: string[], selectedDatabase: string) {
this.sourceDatabaseName = selectedDatabase;
this._onSetSourceDatabaseNames.fire({ databaseNames: options, selectedDatabase: selectedDatabase });
}
/**
* Update target database name
*/
public updateTargetDatabaseName(value: string) {
this.targetDatabaseName = value;
this._onSetTargetDatabaseName.fire(value);
}
}