mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Move DacFx wizard into separate extension (#4115)
* Moved dacfx wizard into separate extension * updating to use azdata * one more azdata change * bump import extension version * renaming extension to dacpac
This commit is contained in:
@@ -1,148 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWizard';
|
||||
import { BasePage } from '../api/basePage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class DacFxSummaryPage extends BasePage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
|
||||
private form: azdata.FormContainer;
|
||||
private table: azdata.TableComponent;
|
||||
private loader: azdata.LoadingComponent;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super();
|
||||
this.instance = instance;
|
||||
this.wizardPage = wizardPage;
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.table = this.view.modelBuilder.table().component();
|
||||
this.loader = this.view.modelBuilder.loadingComponent().withItem(this.table).component();
|
||||
this.form = this.view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.table,
|
||||
title: ''
|
||||
}
|
||||
]
|
||||
).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
this.populateTable();
|
||||
this.loader.loading = false;
|
||||
if (this.model.upgradeExisting && this.model.generateScriptAndDeploy) {
|
||||
this.instance.wizard.generateScriptButton.hidden = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageLeave(): Promise<boolean> {
|
||||
this.instance.wizard.generateScriptButton.hidden = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
if (this.loader.loading) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private populateTable() {
|
||||
let data = [];
|
||||
let targetServer = localize('dacfx.targetServerName', 'Target Server');
|
||||
let targetDatabase = localize('dacfx.targetDatabaseName', 'Target Database');
|
||||
let sourceServer = localize('dacfx.sourceServerName', 'Source Server');
|
||||
let sourceDatabase = localize('dacfx.sourceDatabaseName', 'Source Database');
|
||||
let fileLocation = localize('dacfx.fileLocation', 'File Location');
|
||||
let scriptLocation = localize('dacfx.scriptLocation', 'Deployment Script Location');
|
||||
let action = localize('dacfx.action', 'Action');
|
||||
let deploy = localize('dacfx.deploy', 'Deploy');
|
||||
let generateScript = localize('dacfx.generateScript', 'Generate Deployment Script');
|
||||
|
||||
switch (this.instance.selectedOperation) {
|
||||
case Operation.deploy: {
|
||||
data = [
|
||||
[targetServer, this.model.serverName],
|
||||
[fileLocation, this.model.filePath],
|
||||
[targetDatabase, this.model.database]];
|
||||
if (this.model.generateScriptAndDeploy) {
|
||||
data[3] = [scriptLocation, this.model.scriptFilePath];
|
||||
data[4] = [action, generateScript + ', ' + deploy];
|
||||
}
|
||||
else {
|
||||
data[3] = [action, deploy];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Operation.extract: {
|
||||
data = [
|
||||
[sourceServer, this.model.serverName],
|
||||
[sourceDatabase, this.model.database],
|
||||
[localize('dacfxExtract.version', 'Version'), this.model.version],
|
||||
[fileLocation, this.model.filePath]];
|
||||
break;
|
||||
}
|
||||
case Operation.import: {
|
||||
data = [
|
||||
[targetServer, this.model.serverName],
|
||||
[fileLocation, this.model.filePath],
|
||||
[targetDatabase, this.model.database]];
|
||||
break;
|
||||
}
|
||||
case Operation.export: {
|
||||
data = [
|
||||
[sourceServer, this.model.serverName],
|
||||
[sourceDatabase, this.model.database],
|
||||
[fileLocation, this.model.filePath]];
|
||||
break;
|
||||
}
|
||||
case Operation.generateDeployScript: {
|
||||
data = [
|
||||
[targetServer, this.model.serverName],
|
||||
[fileLocation, this.model.filePath],
|
||||
[targetDatabase, this.model.database],
|
||||
[scriptLocation, this.model.scriptFilePath],
|
||||
[action, generateScript]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.table.updateProperties({
|
||||
data: data,
|
||||
columns: [
|
||||
{
|
||||
value: localize('dacfx.settingColumn', 'Setting'),
|
||||
cssClass: 'align-with-header'
|
||||
},
|
||||
{
|
||||
value: localize('dacfx.valueColumn', 'Value'),
|
||||
cssClass: 'align-with-header'
|
||||
}],
|
||||
width: 700,
|
||||
height: 200
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class DeployActionPage extends DacFxConfigPage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
private deployRadioButton: azdata.RadioButtonComponent;
|
||||
private deployScriptRadioButton: azdata.RadioButtonComponent;
|
||||
private scriptRadioButton: azdata.RadioButtonComponent;
|
||||
private form: azdata.FormContainer;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let deployComponent = await this.createDeployRadioButton();
|
||||
let deployScriptComponent = await this.createDeployScriptRadioButton();
|
||||
let scriptComponent = await this.createScriptRadioButton();
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
deployComponent,
|
||||
scriptComponent,
|
||||
deployScriptComponent,
|
||||
fileBrowserComponent
|
||||
]).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
|
||||
//default have the first radio button checked
|
||||
this.deployRadioButton.checked = true;
|
||||
this.toggleFileBrowser(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
// generate script file path in case the database changed since last time the page was entered
|
||||
this.setDefaultScriptFilePath();
|
||||
return true;
|
||||
}
|
||||
|
||||
private async createDeployRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.deployRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedDeployAction',
|
||||
label: localize('dacFx.deployRadioButtonLabel', 'Deploy'),
|
||||
}).component();
|
||||
|
||||
this.deployRadioButton.onDidClick(() => {
|
||||
this.model.generateScriptAndDeploy = false;
|
||||
this.instance.setDoneButton(Operation.deploy);
|
||||
this.toggleFileBrowser(false);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.deployRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createDeployScriptRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.deployScriptRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedDeployAction',
|
||||
label: localize('dacFx.deployScriptRadioButtonLabel', 'Generate Deployment Script and Deploy'),
|
||||
}).component();
|
||||
|
||||
this.deployScriptRadioButton.onDidClick(() => {
|
||||
this.model.generateScriptAndDeploy = true;
|
||||
this.instance.setDoneButton(Operation.deploy);
|
||||
this.toggleFileBrowser(true);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.deployScriptRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createScriptRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.scriptRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedDeployAction',
|
||||
label: localize('dacFx.scriptRadioButtonLabel', 'Generate Deployment Script'),
|
||||
}).component();
|
||||
|
||||
this.scriptRadioButton.onDidClick(() => {
|
||||
this.model.generateScriptAndDeploy = false;
|
||||
this.toggleFileBrowser(true);
|
||||
|
||||
//change button text and operation
|
||||
this.instance.setDoneButton(Operation.generateDeployScript);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.scriptRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<azdata.FormComponentGroup> {
|
||||
this.createFileBrowserParts();
|
||||
|
||||
//default filepath
|
||||
this.setDefaultScriptFilePath();
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUri = await vscode.window.showSaveDialog(
|
||||
{
|
||||
defaultUri: vscode.Uri.file(this.fileTextBox.value),
|
||||
saveLabel: localize('dacfxDeployScript.saveFile', 'Save'),
|
||||
filters: {
|
||||
'SQL Files': ['sql'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
this.model.scriptFilePath = fileUri.fsPath;
|
||||
});
|
||||
|
||||
this.fileTextBox.onTextChanged(async () => {
|
||||
this.model.scriptFilePath = this.fileTextBox.value;
|
||||
});
|
||||
|
||||
return {
|
||||
title: '',
|
||||
components: [
|
||||
{
|
||||
title: localize('dacfx.generatedScriptLocation', 'Deployment Script Location'),
|
||||
component: this.fileTextBox,
|
||||
layout: {
|
||||
horizontal: true,
|
||||
componentWidth: 400
|
||||
},
|
||||
actions: [this.fileButton]
|
||||
},],
|
||||
};
|
||||
}
|
||||
|
||||
private toggleFileBrowser(enable: boolean): void {
|
||||
this.fileTextBox.enabled = enable;
|
||||
this.fileButton.enabled = enable;
|
||||
}
|
||||
|
||||
private setDefaultScriptFilePath(): void {
|
||||
let now = new Date();
|
||||
let datetime = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate() + '-' + now.getHours() + '-' + now.getMinutes();
|
||||
this.fileTextBox.value = path.join(os.homedir(), this.model.database + '_UpgradeDACScript_' + datetime + '.sql');
|
||||
this.model.scriptFilePath = this.fileTextBox.value;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard, DeployOperationPath, Operation } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class DeployConfigPage extends DacFxConfigPage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
private databaseDropdownComponent: azdata.FormComponent;
|
||||
private databaseComponent: azdata.FormComponent;
|
||||
private formBuilder: azdata.FormBuilder;
|
||||
private form: azdata.FormContainer;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
this.fileExtension = '.bacpac';
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let serverComponent = await this.createServerDropdown(true);
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
this.databaseComponent = await this.createDatabaseTextBox();
|
||||
this.databaseComponent.title = localize('dacFx.databaseNameTextBox', 'Database Name');
|
||||
this.databaseDropdownComponent = await this.createDeployDatabaseDropdown();
|
||||
this.databaseDropdownComponent.title = localize('dacFx.databaseNameDropdown', 'Database Name');
|
||||
let radioButtons = await this.createRadiobuttons();
|
||||
|
||||
this.formBuilder = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
fileBrowserComponent,
|
||||
serverComponent,
|
||||
radioButtons,
|
||||
this.databaseDropdownComponent
|
||||
], {
|
||||
horizontal: true,
|
||||
componentWidth: 400
|
||||
});
|
||||
|
||||
this.form = this.formBuilder.component();
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
let r1 = await this.populateServerDropdown();
|
||||
let r2 = await this.populateDeployDatabaseDropdown();
|
||||
return r1 && r2;
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<azdata.FormComponent> {
|
||||
this.createFileBrowserParts();
|
||||
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUris = await vscode.window.showOpenDialog(
|
||||
{
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
defaultUri: vscode.Uri.file(os.homedir()),
|
||||
openLabel: localize('dacFxDeploy.openFile', 'Open'),
|
||||
filters: {
|
||||
'dacpac Files': ['dacpac'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUris || fileUris.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileUri = fileUris[0];
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
this.model.filePath = fileUri.fsPath;
|
||||
});
|
||||
|
||||
this.fileTextBox.onTextChanged(async () => {
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
this.databaseTextBox.value = this.generateDatabaseName(this.model.filePath);
|
||||
if (!this.model.upgradeExisting) {
|
||||
this.model.database = this.databaseTextBox.value;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.fileTextBox,
|
||||
title: localize('dacFxDeploy.fileTextboxTitle', 'File Location'),
|
||||
actions: [this.fileButton]
|
||||
};
|
||||
}
|
||||
|
||||
private async createRadiobuttons(): Promise<azdata.FormComponent> {
|
||||
let upgradeRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'updateExisting',
|
||||
label: localize('dacFx.upgradeRadioButtonLabel', 'Upgrade Existing Database'),
|
||||
}).component();
|
||||
|
||||
let newRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'updateExisting',
|
||||
label: localize('dacFx.newRadioButtonLabel', 'New Database'),
|
||||
}).component();
|
||||
|
||||
upgradeRadioButton.onDidClick(() => {
|
||||
this.model.upgradeExisting = true;
|
||||
this.formBuilder.removeFormItem(this.databaseComponent);
|
||||
this.formBuilder.addFormItem(this.databaseDropdownComponent, { horizontal: true, componentWidth: 400 });
|
||||
this.model.database = (<azdata.CategoryValue>this.databaseDropdown.value).name;
|
||||
|
||||
// add deploy plan and generate script pages
|
||||
let deployPlanPage = this.instance.pages.get('deployPlan');
|
||||
this.instance.wizard.addPage(deployPlanPage.wizardPage, DeployOperationPath.deployPlan);
|
||||
let deployActionPage = this.instance.pages.get('deployAction');
|
||||
this.instance.wizard.addPage(deployActionPage.wizardPage, DeployOperationPath.deployAction);
|
||||
});
|
||||
|
||||
newRadioButton.onDidClick(() => {
|
||||
this.model.upgradeExisting = false;
|
||||
this.formBuilder.removeFormItem(this.databaseDropdownComponent);
|
||||
this.formBuilder.addFormItem(this.databaseComponent, { horizontal: true, componentWidth: 400 });
|
||||
this.model.database = this.databaseTextBox.value;
|
||||
this.instance.setDoneButton(Operation.deploy);
|
||||
|
||||
// remove deploy plan and generate script pages
|
||||
this.instance.wizard.removePage(DeployOperationPath.deployAction);
|
||||
this.instance.wizard.removePage(DeployOperationPath.deployPlan);
|
||||
});
|
||||
|
||||
//Initialize with upgrade existing true
|
||||
upgradeRadioButton.checked = true;
|
||||
this.model.upgradeExisting = true;
|
||||
|
||||
let flexRadioButtonsModel = this.view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'row',
|
||||
}).withItems([
|
||||
upgradeRadioButton, newRadioButton]
|
||||
).component();
|
||||
|
||||
return {
|
||||
component: flexRadioButtonsModel,
|
||||
title: localize('dacFx.targetDatabaseRadioButtonsTitle', 'Target Database')
|
||||
};
|
||||
}
|
||||
|
||||
protected async createDeployDatabaseDropdown(): Promise<azdata.FormComponent> {
|
||||
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
//Handle database changes
|
||||
this.databaseDropdown.onValueChanged(async () => {
|
||||
this.model.database = (<azdata.CategoryValue>this.databaseDropdown.value).name;
|
||||
});
|
||||
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
|
||||
return {
|
||||
component: this.databaseLoader,
|
||||
title: localize('dacFx.targetDatabaseDropdownTitle', 'Database Name')
|
||||
};
|
||||
}
|
||||
|
||||
protected async populateDeployDatabaseDropdown(): Promise<boolean> {
|
||||
this.databaseLoader.loading = true;
|
||||
this.databaseDropdown.updateProperties({ values: [] });
|
||||
if (!this.model.server) {
|
||||
this.databaseLoader.loading = false;
|
||||
return false;
|
||||
}
|
||||
let values = await this.getDatabaseValues();
|
||||
|
||||
//set the database to the first dropdown value if upgrading, otherwise it should get set to the textbox value
|
||||
if (this.model.upgradeExisting) {
|
||||
this.model.database = values[0].name;
|
||||
}
|
||||
|
||||
this.databaseDropdown.updateProperties({
|
||||
values: values
|
||||
});
|
||||
this.databaseLoader.loading = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private generateDatabaseName(filePath: string): string {
|
||||
let result = path.parse(filePath);
|
||||
return result.name;
|
||||
}
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as parser from 'htmlparser2';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export enum deployPlanXml {
|
||||
AlertElement = 'Alert',
|
||||
OperationElement = 'Operation',
|
||||
ItemElement = 'Item',
|
||||
NameAttribute = 'Name',
|
||||
ValueAttribute = 'Value',
|
||||
TypeAttribute = 'Type',
|
||||
IdAttribute = 'Id',
|
||||
DataIssueAttribute = 'DataIssue'
|
||||
}
|
||||
|
||||
export class TableObject {
|
||||
operation: string;
|
||||
object: string;
|
||||
type: string;
|
||||
dataloss: boolean;
|
||||
}
|
||||
|
||||
export class DeployPlanResult {
|
||||
columnData: Array<Array<string>>;
|
||||
dataLossAlerts: Set<string>;
|
||||
}
|
||||
|
||||
export class DeployPlanPage extends DacFxConfigPage {
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
private formBuilder: azdata.FormBuilder;
|
||||
private form: azdata.FormContainer;
|
||||
private table: azdata.TableComponent;
|
||||
private loader: azdata.LoadingComponent;
|
||||
private dataLossCheckbox: azdata.CheckBoxComponent;
|
||||
private dataLossText: azdata.TextComponent;
|
||||
private dataLossComponentGroup: azdata.FormComponentGroup;
|
||||
private noDataLossTextComponent: azdata.FormComponent;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.table = this.view.modelBuilder.table().component();
|
||||
this.loader = this.view.modelBuilder.loadingComponent().withItem(this.table).component();
|
||||
this.dataLossComponentGroup = await this.createDataLossComponents();
|
||||
this.noDataLossTextComponent = await this.createNoDataLossText();
|
||||
|
||||
this.formBuilder = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.loader,
|
||||
title: ''
|
||||
},
|
||||
this.dataLossComponentGroup
|
||||
], {
|
||||
horizontal: true,
|
||||
});
|
||||
this.form = this.formBuilder.component();
|
||||
await this.view.initializeModel(this.form);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
// reset checkbox settings
|
||||
this.formBuilder.addFormItem(this.dataLossComponentGroup, { horizontal: true, componentWidth: 400 });
|
||||
this.dataLossCheckbox.checked = false;
|
||||
this.dataLossCheckbox.enabled = false;
|
||||
this.formBuilder.removeFormItem(this.noDataLossTextComponent);
|
||||
|
||||
this.loader.loading = true;
|
||||
this.table.data = [];
|
||||
await this.populateTable();
|
||||
this.loader.loading = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async populateTable() {
|
||||
let report = await this.instance.generateDeployPlan();
|
||||
let result = this.parseXml(report);
|
||||
|
||||
this.table.updateProperties({
|
||||
data: this.getColumnData(result),
|
||||
columns: this.getTableColumns(result.dataLossAlerts.size > 0),
|
||||
width: 875,
|
||||
height: 300
|
||||
});
|
||||
|
||||
if (result.dataLossAlerts.size > 0) {
|
||||
// update message to list how many operations could result in data loss
|
||||
this.dataLossText.updateProperties({
|
||||
value: localize('dacfx.dataLossTextWithCount', '{0} of the deploy actions listed may result in data loss. Please ensure you have a backup or snapshot available in the event of an issue with the deployment.', result.dataLossAlerts.size)
|
||||
});
|
||||
this.dataLossCheckbox.enabled = true;
|
||||
} else {
|
||||
// check checkbox to enable Next button and remove checkbox because there won't be any possible data loss
|
||||
this.dataLossCheckbox.checked = true;
|
||||
this.formBuilder.removeFormItem(this.dataLossComponentGroup);
|
||||
this.formBuilder.addFormItem(this.noDataLossTextComponent, { componentWidth: 300, horizontal: true });
|
||||
}
|
||||
}
|
||||
|
||||
private async createDataLossCheckbox(): Promise<azdata.FormComponent> {
|
||||
this.dataLossCheckbox = this.view.modelBuilder.checkBox()
|
||||
.withValidation(component => component.checked === true)
|
||||
.withProperties({
|
||||
label: localize('dacFx.dataLossCheckbox', 'Proceed despite possible data loss'),
|
||||
}).component();
|
||||
|
||||
return {
|
||||
component: this.dataLossCheckbox,
|
||||
title: '',
|
||||
required: true
|
||||
};
|
||||
}
|
||||
|
||||
private async createNoDataLossText(): Promise<azdata.FormComponent> {
|
||||
let noDataLossText = this.view.modelBuilder.text()
|
||||
.withProperties({
|
||||
value: localize('dacfx.noDataLossText', 'No data loss will occur from the listed deploy actions.')
|
||||
}).component();
|
||||
|
||||
return {
|
||||
title: '',
|
||||
component: noDataLossText
|
||||
};
|
||||
}
|
||||
|
||||
private async createDataLossComponents(): Promise<azdata.FormComponentGroup> {
|
||||
let dataLossComponent = await this.createDataLossCheckbox();
|
||||
this.dataLossText = this.view.modelBuilder.text()
|
||||
.withProperties({
|
||||
value: localize('dacfx.dataLossText', 'The deploy actions may result in data loss. Please ensure you have a backup or snapshot available in the event of an issue with the deployment.')
|
||||
}).component();
|
||||
|
||||
return {
|
||||
title: '',
|
||||
components: [
|
||||
{
|
||||
component: this.dataLossText,
|
||||
layout: {
|
||||
componentWidth: 400,
|
||||
horizontal: true
|
||||
},
|
||||
title: ''
|
||||
},
|
||||
dataLossComponent
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
private getColumnData(result: DeployPlanResult): Array<Array<string>> {
|
||||
// remove data loss column data if there aren't any alerts
|
||||
let columnData = result.columnData;
|
||||
if (result.dataLossAlerts.size === 0) {
|
||||
columnData.forEach(entry => {
|
||||
entry.shift();
|
||||
});
|
||||
}
|
||||
return columnData;
|
||||
}
|
||||
|
||||
private getTableColumns(dataloss: boolean): azdata.TableColumn[] {
|
||||
let columns: azdata.TableColumn[] = [
|
||||
{
|
||||
value: localize('dacfx.operationColumn', 'Operation'),
|
||||
width: 75,
|
||||
cssClass: 'align-with-header',
|
||||
toolTip: localize('dacfx.operationTooltip', 'Operation(Create, Alter, Delete) that will occur during deployment')
|
||||
},
|
||||
{
|
||||
value: localize('dacfx.typeColumn', 'Type'),
|
||||
width: 100,
|
||||
cssClass: 'align-with-header',
|
||||
toolTip: localize('dacfx.typeTooltip', 'Type of object that will be affected by deployment')
|
||||
},
|
||||
{
|
||||
value: localize('dacfx.objectColumn', 'Object'),
|
||||
width: 300,
|
||||
cssClass: 'align-with-header',
|
||||
toolTip: localize('dacfx.objecTooltip', 'Name of object that will be affected by deployment')
|
||||
}];
|
||||
|
||||
if (dataloss) {
|
||||
columns.unshift(
|
||||
{
|
||||
value: localize('dacfx.dataLossColumn', 'Data Loss'),
|
||||
width: 50,
|
||||
cssClass: 'center-align',
|
||||
toolTip: localize('dacfx.dataLossTooltip', 'Operations that may result in data loss are marked with a warning sign')
|
||||
});
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
private parseXml(report: string): DeployPlanResult {
|
||||
let operations = new Array<TableObject>();
|
||||
let dataLossAlerts = new Set<string>();
|
||||
|
||||
let currentOperation = '';
|
||||
let dataIssueAlert = false;
|
||||
let currentReportSection: deployPlanXml;
|
||||
let currentTableObj: TableObject;
|
||||
let p = new parser.Parser({
|
||||
onopentagname(name) {
|
||||
if (name === deployPlanXml.AlertElement) {
|
||||
currentReportSection = deployPlanXml.AlertElement;
|
||||
} else if (name === deployPlanXml.OperationElement) {
|
||||
currentReportSection = deployPlanXml.OperationElement;
|
||||
} else if (name === deployPlanXml.ItemElement) {
|
||||
currentTableObj = new TableObject();
|
||||
}
|
||||
},
|
||||
onattribute: function (name, value) {
|
||||
if (currentReportSection === deployPlanXml.AlertElement) {
|
||||
switch (name) {
|
||||
case deployPlanXml.NameAttribute: {
|
||||
// only care about showing data loss alerts
|
||||
if (value === deployPlanXml.DataIssueAttribute) {
|
||||
dataIssueAlert = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case deployPlanXml.IdAttribute: {
|
||||
if (dataIssueAlert) {
|
||||
dataLossAlerts.add(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (currentReportSection === deployPlanXml.OperationElement) {
|
||||
switch (name) {
|
||||
case deployPlanXml.NameAttribute: {
|
||||
currentOperation = value;
|
||||
break;
|
||||
}
|
||||
case deployPlanXml.ValueAttribute: {
|
||||
currentTableObj.object = value;
|
||||
break;
|
||||
}
|
||||
case deployPlanXml.TypeAttribute: {
|
||||
currentTableObj.type = value;
|
||||
break;
|
||||
}
|
||||
case deployPlanXml.IdAttribute: {
|
||||
if (dataLossAlerts.has(value)) {
|
||||
currentTableObj.dataloss = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onclosetag: function (name) {
|
||||
if (name === deployPlanXml.ItemElement) {
|
||||
currentTableObj.operation = currentOperation;
|
||||
operations.push(currentTableObj);
|
||||
}
|
||||
}
|
||||
}, { xmlMode: true, decodeEntities: true });
|
||||
p.parseChunk(report);
|
||||
|
||||
let data = new Array<Array<string>>();
|
||||
operations.forEach(operation => {
|
||||
let isDataLoss = operation.dataloss ? '⚠️' : '';
|
||||
data.push([isDataLoss, operation.operation, operation.type, operation.object]);
|
||||
});
|
||||
|
||||
return {
|
||||
columnData: data,
|
||||
dataLossAlerts: dataLossAlerts
|
||||
};
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ExportConfigPage extends DacFxConfigPage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
|
||||
private form: azdata.FormContainer;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
this.fileExtension = '.bacpac';
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let databaseComponent = await this.createDatabaseDropdown();
|
||||
let serverComponent = await this.createServerDropdown(false);
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
serverComponent,
|
||||
databaseComponent,
|
||||
fileBrowserComponent,
|
||||
], {
|
||||
horizontal: true,
|
||||
componentWidth: 400
|
||||
}).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
let r1 = await this.populateServerDropdown();
|
||||
let r2 = await this.populateDatabaseDropdown();
|
||||
return r1 && r2;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
if (this.databaseLoader.loading) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<azdata.FormComponent> {
|
||||
this.createFileBrowserParts();
|
||||
|
||||
// default filepath
|
||||
this.fileTextBox.value = this.generateFilePath();
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUri = await vscode.window.showSaveDialog(
|
||||
{
|
||||
defaultUri: vscode.Uri.file(this.fileTextBox.value),
|
||||
saveLabel: localize('dacfxExport.saveFile', 'Save'),
|
||||
filters: {
|
||||
'bacpac Files': ['bacpac'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
this.model.filePath = fileUri.fsPath;
|
||||
});
|
||||
|
||||
this.fileTextBox.onTextChanged(async () => {
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.fileTextBox,
|
||||
title: localize('dacFxExport.fileTextboxTitle', 'File Location'),
|
||||
actions: [this.fileButton]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ExtractConfigPage extends DacFxConfigPage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
|
||||
private form: azdata.FormContainer;
|
||||
private versionTextBox: azdata.InputBoxComponent;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
this.fileExtension = '.dacpac';
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let databaseComponent = await this.createDatabaseDropdown();
|
||||
let serverComponent = await this.createServerDropdown(false);
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
let versionComponent = await this.createVersionTextBox();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
serverComponent,
|
||||
databaseComponent,
|
||||
versionComponent,
|
||||
fileBrowserComponent,
|
||||
], {
|
||||
horizontal: true,
|
||||
componentWidth: 400
|
||||
}).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
let r1 = await this.populateServerDropdown();
|
||||
let r2 = await this.populateDatabaseDropdown();
|
||||
return r1 && r2;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
if (this.databaseLoader.loading) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<azdata.FormComponent> {
|
||||
this.createFileBrowserParts();
|
||||
|
||||
// default filepath
|
||||
this.fileTextBox.value = this.generateFilePath();
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUri = await vscode.window.showSaveDialog(
|
||||
{
|
||||
defaultUri: vscode.Uri.file(this.fileTextBox.value),
|
||||
saveLabel: localize('dacfxExtract.saveFile', 'Save'),
|
||||
filters: {
|
||||
'dacpac Files': ['dacpac'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
this.model.filePath = fileUri.fsPath;
|
||||
});
|
||||
|
||||
this.fileTextBox.onTextChanged(async () => {
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.fileTextBox,
|
||||
title: localize('dacFxExtract.fileTextboxTitle', 'File Location'),
|
||||
actions: [this.fileButton]
|
||||
};
|
||||
}
|
||||
|
||||
private async createVersionTextBox(): Promise<azdata.FormComponent> {
|
||||
this.versionTextBox = this.view.modelBuilder.inputBox().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
// default filepath
|
||||
this.versionTextBox.value = '1.0.0.0';
|
||||
this.model.version = this.versionTextBox.value;
|
||||
|
||||
this.versionTextBox.onTextChanged(async () => {
|
||||
this.model.version = this.versionTextBox.value;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.versionTextBox,
|
||||
title: localize('dacFxExtract.versionTextboxTitle', 'Version (use x.x.x.x where x is a number)'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ImportConfigPage extends DacFxConfigPage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
|
||||
private form: azdata.FormContainer;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super(instance, wizardPage, model, view);
|
||||
this.fileExtension = '.bacpac';
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let databaseComponent = await this.createDatabaseTextBox();
|
||||
let serverComponent = await this.createServerDropdown(true);
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
fileBrowserComponent,
|
||||
serverComponent,
|
||||
databaseComponent,
|
||||
], {
|
||||
horizontal: true,
|
||||
componentWidth: 400
|
||||
}).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
let r1 = await this.populateServerDropdown();
|
||||
return r1;
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<azdata.FormComponent> {
|
||||
this.createFileBrowserParts();
|
||||
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUris = await vscode.window.showOpenDialog(
|
||||
{
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
defaultUri: vscode.Uri.file(os.homedir()),
|
||||
openLabel: localize('dacFxImport.openFile', 'Open'),
|
||||
filters: {
|
||||
'bacpac Files': ['bacpac'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUris || fileUris.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileUri = fileUris[0];
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
this.model.filePath = fileUri.fsPath;
|
||||
this.model.database = this.generateDatabaseName(this.model.filePath);
|
||||
this.databaseTextBox.value = this.model.database;
|
||||
});
|
||||
|
||||
this.fileTextBox.onTextChanged(async () => {
|
||||
this.model.filePath = this.fileTextBox.value;
|
||||
this.model.database = this.generateDatabaseName(this.model.filePath);
|
||||
this.databaseTextBox.value = this.model.database;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.fileTextBox,
|
||||
title: localize('dacFxImport.fileTextboxTitle', 'File Location'),
|
||||
actions: [this.fileButton]
|
||||
};
|
||||
}
|
||||
|
||||
private generateDatabaseName(filePath: string): string {
|
||||
let result = path.parse(filePath);
|
||||
return result.name;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { DacFxDataModel } from '../api/models';
|
||||
import { DataTierApplicationWizard, Operation, DeployOperationPath, ExtractOperationPath, ImportOperationPath, ExportOperationPath } from '../dataTierApplicationWizard';
|
||||
import { BasePage } from '../api/basePage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class SelectOperationPage extends BasePage {
|
||||
|
||||
protected readonly wizardPage: azdata.window.WizardPage;
|
||||
protected readonly instance: DataTierApplicationWizard;
|
||||
protected readonly model: DacFxDataModel;
|
||||
protected readonly view: azdata.ModelView;
|
||||
|
||||
private deployRadioButton: azdata.RadioButtonComponent;
|
||||
private extractRadioButton: azdata.RadioButtonComponent;
|
||||
private importRadioButton: azdata.RadioButtonComponent;
|
||||
private exportRadioButton: azdata.RadioButtonComponent;
|
||||
private form: azdata.FormContainer;
|
||||
|
||||
public constructor(instance: DataTierApplicationWizard, wizardPage: azdata.window.WizardPage, model: DacFxDataModel, view: azdata.ModelView) {
|
||||
super();
|
||||
this.instance = instance;
|
||||
this.wizardPage = wizardPage;
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let deployComponent = await this.createDeployRadioButton();
|
||||
let extractComponent = await this.createExtractRadioButton();
|
||||
let importComponent = await this.createImportRadioButton();
|
||||
let exportComponent = await this.createExportRadioButton();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
deployComponent,
|
||||
extractComponent,
|
||||
importComponent,
|
||||
exportComponent
|
||||
], {
|
||||
horizontal: true
|
||||
}).component();
|
||||
await this.view.initializeModel(this.form);
|
||||
|
||||
// default have the first radio button checked
|
||||
this.deployRadioButton.checked = true;
|
||||
this.instance.setDoneButton(Operation.deploy);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async createDeployRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.deployRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedOperation',
|
||||
label: localize('dacFx.deployRadioButtonLabel', 'Deploy a data-tier application .dacpac file to an instance of SQL Server [Deploy Dacpac]'),
|
||||
}).component();
|
||||
|
||||
this.deployRadioButton.onDidClick(() => {
|
||||
this.removePages();
|
||||
|
||||
//add deploy pages
|
||||
let configPage = this.instance.pages.get('deployConfig');
|
||||
this.instance.wizard.addPage(configPage.wizardPage, DeployOperationPath.deployOptions);
|
||||
let deployPlanPage = this.instance.pages.get('deployPlan');
|
||||
this.instance.wizard.addPage(deployPlanPage.wizardPage, DeployOperationPath.deployPlan);
|
||||
let actionPage = this.instance.pages.get('deployAction');
|
||||
this.instance.wizard.addPage(actionPage.wizardPage, DeployOperationPath.deployAction);
|
||||
this.addSummaryPage(DeployOperationPath.summary);
|
||||
|
||||
// change button text and operation
|
||||
this.instance.setDoneButton(Operation.deploy);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.deployRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createExtractRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.extractRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedOperation',
|
||||
label: localize('dacFx.extractRadioButtonLabel', 'Extract a data-tier application from an instance of SQL Server to a .dacpac file [Extract Dacpac]'),
|
||||
}).component();
|
||||
|
||||
this.extractRadioButton.onDidClick(() => {
|
||||
this.removePages();
|
||||
|
||||
// add the extract page
|
||||
let page = this.instance.pages.get('extractConfig');
|
||||
this.instance.wizard.addPage(page.wizardPage, ExtractOperationPath.options);
|
||||
this.addSummaryPage(ExtractOperationPath.summary);
|
||||
|
||||
// change button text and operation
|
||||
this.instance.setDoneButton(Operation.extract);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.extractRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createImportRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.importRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedOperation',
|
||||
label: localize('dacFx.importRadioButtonLabel', 'Create a database from a .bacpac file [Import Bacpac]'),
|
||||
}).component();
|
||||
|
||||
this.importRadioButton.onDidClick(() => {
|
||||
this.removePages();
|
||||
|
||||
// add the import page
|
||||
let page = this.instance.pages.get('importConfig');
|
||||
this.instance.wizard.addPage(page.wizardPage, ImportOperationPath.options);
|
||||
this.addSummaryPage(ImportOperationPath.summary);
|
||||
|
||||
// change button text and operation
|
||||
this.instance.setDoneButton(Operation.import);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.importRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private async createExportRadioButton(): Promise<azdata.FormComponent> {
|
||||
this.exportRadioButton = this.view.modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
name: 'selectedOperation',
|
||||
label: localize('dacFx.exportRadioButtonLabel', 'Export the schema and data from a database to the logical .bacpac file format [Export Bacpac]'),
|
||||
}).component();
|
||||
|
||||
this.exportRadioButton.onDidClick(() => {
|
||||
this.removePages();
|
||||
|
||||
// add the export pages
|
||||
let page = this.instance.pages.get('exportConfig');
|
||||
this.instance.wizard.addPage(page.wizardPage, ExportOperationPath.options);
|
||||
this.addSummaryPage(ExportOperationPath.summary);
|
||||
|
||||
// change button text and operation
|
||||
this.instance.setDoneButton(Operation.export);
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.exportRadioButton,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
private removePages() {
|
||||
let numPages = this.instance.wizard.pages.length;
|
||||
for (let i = numPages - 1; i > 0; --i) {
|
||||
this.instance.wizard.removePage(i);
|
||||
}
|
||||
}
|
||||
|
||||
private addSummaryPage(index: number) {
|
||||
let summaryPage = this.instance.pages.get('summary');
|
||||
this.instance.wizard.addPage(summaryPage.wizardPage, index);
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator(() => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user