mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 18:48:33 -05:00
* Add CodeQL Analysis workflow (#10195) * Add CodeQL Analysis workflow * Fix path * dashboard refactor * update version, readme, minor ui changes * fix merge issue * Revert "Add CodeQL Analysis workflow (#10195)" This reverts commit fe98d586cd75be4758ac544649bb4983accf4acd. * fix context switching issue * fix resource id parsing error and mi api version * mv refresh btn, rm autorefresh, align cards * remove missed autorefresh code * improve error handling and messages * fix typos * remove duplicate/unnecessary _populate* calls * change clear configuration button text * remove confusing watermark text * add stale account handling Co-authored-by: Justin Hutchings <jhutchings1@users.noreply.github.com>
313 lines
9.8 KiB
TypeScript
313 lines
9.8 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as azdata from 'azdata';
|
|
import * as vscode from 'vscode';
|
|
import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel';
|
|
import * as constants from '../../constants/strings';
|
|
import { getMigrationTargetInstance, SqlManagedInstance } from '../../api/azure';
|
|
import { IconPathHelper } from '../../constants/iconPathHelper';
|
|
import { convertByteSizeToReadableUnit, get12HourTime } from '../../api/utils';
|
|
import * as styles from '../../constants/styles';
|
|
import { isBlobMigration } from '../../constants/helper';
|
|
export class ConfirmCutoverDialog {
|
|
private _dialogObject!: azdata.window.Dialog;
|
|
private _view!: azdata.ModelView;
|
|
private _disposables: vscode.Disposable[] = [];
|
|
|
|
constructor(private migrationCutoverModel: MigrationCutoverDialogModel) {
|
|
this._dialogObject = azdata.window.createModelViewDialog('', 'ConfirmCutoverDialog', 500);
|
|
}
|
|
|
|
async initialize(): Promise<void> {
|
|
const tab = azdata.window.createTab('');
|
|
tab.registerContent(async (view: azdata.ModelView) => {
|
|
this._view = view;
|
|
|
|
const completeCutoverText = view.modelBuilder.text().withProps({
|
|
value: constants.COMPLETE_CUTOVER,
|
|
CSSStyles: { ...styles.PAGE_TITLE_CSS }
|
|
}).component();
|
|
|
|
const sourceDatabaseText = view.modelBuilder.text().withProps({
|
|
value: this.migrationCutoverModel._migration.properties.sourceDatabaseName,
|
|
CSSStyles: {
|
|
...styles.SMALL_NOTE_CSS,
|
|
'margin': '4px 0px 8px'
|
|
}
|
|
}).component();
|
|
|
|
const separator = this._view.modelBuilder.separator().withProps({ width: '800px' }).component();
|
|
const helpMainText = this._view.modelBuilder.text().withProps({
|
|
value: constants.CUTOVER_HELP_MAIN,
|
|
CSSStyles: { ...styles.BODY_CSS }
|
|
}).component();
|
|
|
|
const helpStepsText = this._view.modelBuilder.text().withProps({
|
|
value: this.migrationCutoverModel.confirmCutoverStepsString(),
|
|
CSSStyles: {
|
|
...styles.BODY_CSS,
|
|
'padding': '8px'
|
|
}
|
|
}).component();
|
|
|
|
const fileContainer = isBlobMigration(this.migrationCutoverModel.migrationStatus)
|
|
? this.createBlobFileContainer()
|
|
: this.createNetworkShareFileContainer();
|
|
|
|
const confirmCheckbox = this._view.modelBuilder.checkBox().withProps({
|
|
CSSStyles: {
|
|
...styles.BODY_CSS,
|
|
'margin-bottom': '12px'
|
|
},
|
|
label: constants.CONFIRM_CUTOVER_CHECKBOX,
|
|
}).component();
|
|
|
|
this._disposables.push(confirmCheckbox.onChanged(e => {
|
|
this._dialogObject.okButton.enabled = e;
|
|
}));
|
|
|
|
const cutoverWarning = this._view.modelBuilder.infoBox().withProps({
|
|
text: constants.COMPLETING_CUTOVER_WARNING,
|
|
style: 'warning',
|
|
CSSStyles: { ...styles.BODY_CSS }
|
|
}).component();
|
|
|
|
let infoDisplay = 'none';
|
|
if (this.migrationCutoverModel._migration.id.toLocaleLowerCase().includes('managedinstances')) {
|
|
const targetInstance = await getMigrationTargetInstance(
|
|
this.migrationCutoverModel._serviceConstext.azureAccount!,
|
|
this.migrationCutoverModel._serviceConstext.subscription!,
|
|
this.migrationCutoverModel._migration);
|
|
|
|
if ((<SqlManagedInstance>targetInstance)?.sku?.tier === 'BusinessCritical') {
|
|
infoDisplay = 'inline';
|
|
}
|
|
}
|
|
|
|
const businessCriticalInfoBox = this._view.modelBuilder.infoBox().withProps({
|
|
text: constants.BUSINESS_CRITICAL_INFO,
|
|
style: 'information',
|
|
CSSStyles: {
|
|
...styles.BODY_CSS,
|
|
'display': infoDisplay
|
|
}
|
|
}).component();
|
|
|
|
const container = this._view.modelBuilder.flexContainer().withLayout({
|
|
flexFlow: 'column'
|
|
}).withItems([
|
|
completeCutoverText,
|
|
sourceDatabaseText,
|
|
separator,
|
|
helpMainText,
|
|
helpStepsText,
|
|
fileContainer,
|
|
confirmCheckbox,
|
|
cutoverWarning,
|
|
businessCriticalInfoBox
|
|
]).component();
|
|
|
|
this._dialogObject.okButton.enabled = false;
|
|
this._dialogObject.okButton.label = constants.COMPLETE_CUTOVER;
|
|
this._disposables.push(this._dialogObject.okButton.onClick(async (e) => {
|
|
await this.migrationCutoverModel.startCutover();
|
|
void vscode.window.showInformationMessage(
|
|
constants.CUTOVER_IN_PROGRESS(
|
|
this.migrationCutoverModel._migration.properties.sourceDatabaseName));
|
|
}));
|
|
|
|
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
|
[{ component: container }],
|
|
{ horizontal: false }
|
|
);
|
|
const form = formBuilder.withLayout({ width: '100%' }).component();
|
|
|
|
this._disposables.push(this._view.onClosed(e => {
|
|
this._disposables.forEach(
|
|
d => { try { d.dispose(); } catch { } });
|
|
}));
|
|
|
|
return view.initializeModel(form);
|
|
});
|
|
this._dialogObject.content = [tab];
|
|
azdata.window.openDialog(this._dialogObject);
|
|
}
|
|
|
|
private createBlobFileContainer(): azdata.FlexContainer {
|
|
const container = this._view.modelBuilder.flexContainer().withProps({
|
|
CSSStyles: { 'margin': '8px 0' }
|
|
}).component();
|
|
const containerHeading = this._view.modelBuilder.text().withProps({
|
|
value: constants.PENDING_BACKUPS(this.migrationCutoverModel.getPendingLogBackupsCount() ?? 0),
|
|
width: 250,
|
|
CSSStyles: { ...styles.LABEL_CSS }
|
|
}).component();
|
|
container.addItem(containerHeading, { flex: '0' });
|
|
|
|
const refreshButton = this._view.modelBuilder.button().withProps({
|
|
iconPath: IconPathHelper.refresh,
|
|
iconHeight: 16,
|
|
iconWidth: 16,
|
|
width: 70,
|
|
height: 20,
|
|
label: constants.REFRESH,
|
|
}).component();
|
|
this._disposables.push(refreshButton.onDidClick(async e => {
|
|
refreshLoader.loading = true;
|
|
try {
|
|
await this.migrationCutoverModel.fetchStatus();
|
|
containerHeading.value = constants.PENDING_BACKUPS(this.migrationCutoverModel.getPendingLogBackupsCount() ?? 0);
|
|
} catch (e) {
|
|
this._dialogObject.message = {
|
|
level: azdata.window.MessageLevel.Error,
|
|
text: e.toString()
|
|
};
|
|
} finally {
|
|
refreshLoader.loading = false;
|
|
}
|
|
}));
|
|
container.addItem(refreshButton, { flex: '0' });
|
|
|
|
const refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
|
loading: false,
|
|
CSSStyles: {
|
|
'margin-top': '-4px',
|
|
'margin-left': '8px'
|
|
}
|
|
}).component();
|
|
container.addItem(refreshLoader, { flex: '0' });
|
|
|
|
return container;
|
|
}
|
|
|
|
private createNetworkShareFileContainer(): azdata.FlexContainer {
|
|
const container = this._view.modelBuilder.flexContainer().withLayout({
|
|
flexFlow: 'column'
|
|
}).component();
|
|
|
|
const headingRow = this._view.modelBuilder.flexContainer().withLayout({
|
|
flexFlow: 'row'
|
|
}).component();
|
|
|
|
let expanded: boolean = false;
|
|
const containerHeading = this._view.modelBuilder.button().withProps({
|
|
label: constants.PENDING_BACKUPS(this.migrationCutoverModel.getPendingLogBackupsCount() ?? 0),
|
|
width: 220,
|
|
height: 14,
|
|
iconHeight: 12,
|
|
iconWidth: 8,
|
|
iconPath: IconPathHelper.expandButtonClosed,
|
|
CSSStyles: {
|
|
...styles.LABEL_CSS,
|
|
'margin': '16px 8px 0px 0px'
|
|
}
|
|
}).component();
|
|
|
|
this._disposables.push(containerHeading.onDidClick(async e => {
|
|
if (expanded) {
|
|
containerHeading.iconPath = IconPathHelper.expandButtonClosed;
|
|
containerHeading.iconHeight = 12;
|
|
await fileTable.updateCssStyles({ 'display': 'none' });
|
|
} else {
|
|
containerHeading.iconPath = IconPathHelper.expandButtonOpen;
|
|
containerHeading.iconHeight = 8;
|
|
await fileTable.updateCssStyles({ 'display': 'inline' });
|
|
}
|
|
expanded = !expanded;
|
|
}));
|
|
|
|
const refreshButton = this._view.modelBuilder.button().withProps({
|
|
iconPath: IconPathHelper.refresh,
|
|
iconHeight: 16,
|
|
iconWidth: 16,
|
|
width: 70,
|
|
height: 20,
|
|
label: constants.REFRESH,
|
|
CSSStyles: { 'margin-top': '13px' }
|
|
}).component();
|
|
|
|
headingRow.addItem(containerHeading, { flex: '0' });
|
|
|
|
this._disposables.push(refreshButton.onDidClick(async e => {
|
|
refreshLoader.loading = true;
|
|
try {
|
|
await this.migrationCutoverModel.fetchStatus();
|
|
containerHeading.label = constants.PENDING_BACKUPS(this.migrationCutoverModel.getPendingLogBackupsCount() ?? 0);
|
|
lastScanCompleted.value = constants.LAST_SCAN_COMPLETED(get12HourTime(new Date()));
|
|
this.refreshFileTable(fileTable);
|
|
} catch (e) {
|
|
this._dialogObject.message = {
|
|
level: azdata.window.MessageLevel.Error,
|
|
text: e.toString()
|
|
};
|
|
} finally {
|
|
refreshLoader.loading = false;
|
|
}
|
|
}));
|
|
headingRow.addItem(refreshButton, { flex: '0' });
|
|
|
|
const refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
|
loading: false,
|
|
CSSStyles: {
|
|
'margin-top': '15px',
|
|
'margin-left': '5px',
|
|
'height': '13px'
|
|
}
|
|
}).component();
|
|
headingRow.addItem(refreshLoader, { flex: '0' });
|
|
container.addItem(headingRow);
|
|
|
|
const lastScanCompleted = this._view.modelBuilder.text().withProps({
|
|
value: constants.LAST_SCAN_COMPLETED(get12HourTime(new Date())),
|
|
CSSStyles: { ...styles.NOTE_CSS }
|
|
}).component();
|
|
container.addItem(lastScanCompleted);
|
|
|
|
const fileTable = this._view.modelBuilder.table().withProps({
|
|
columns: [
|
|
{
|
|
value: constants.FILE_NAME,
|
|
type: azdata.ColumnType.text,
|
|
width: 250
|
|
},
|
|
{
|
|
value: constants.STATUS,
|
|
type: azdata.ColumnType.text,
|
|
width: 80
|
|
},
|
|
{
|
|
value: constants.SIZE_COLUMN_HEADER,
|
|
type: azdata.ColumnType.text,
|
|
width: 70
|
|
}
|
|
],
|
|
data: [],
|
|
width: 400,
|
|
height: 150,
|
|
CSSStyles: { 'display': 'none' }
|
|
}).component();
|
|
container.addItem(fileTable);
|
|
this.refreshFileTable(fileTable);
|
|
return container;
|
|
}
|
|
|
|
private refreshFileTable(fileTable: azdata.TableComponent) {
|
|
const pendingFiles = this.migrationCutoverModel.getPendingFiles();
|
|
if (pendingFiles.length > 0) {
|
|
fileTable.data = pendingFiles.map(f => {
|
|
return [
|
|
f.fileName,
|
|
f.status,
|
|
convertByteSizeToReadableUnit(f.totalSize)
|
|
];
|
|
});
|
|
} else {
|
|
fileTable.data = [[constants.NO_PENDING_BACKUPS]];
|
|
}
|
|
|
|
}
|
|
}
|