mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Add Deploy Plan page to DacFx wizard (#3911)
* upgrade plan is piped through and returns the xml plan * Added review deploy plan page * checkbox validation now working and columns formatted * formatting and cleaning up code * refactored populateTable() * addressing comments * addressing comments * updating tooltips * add padding to table cells to align with headers * fix problems when going back and forth between pages and changing config options * bump sqltoolsservice version to 71 * fix localization
This commit is contained in:
@@ -8,6 +8,7 @@ import * as nls from 'vscode-nls';
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { SelectOperationPage } from './pages/selectOperationpage';
|
import { SelectOperationPage } from './pages/selectOperationpage';
|
||||||
import { DeployConfigPage } from './pages/deployConfigPage';
|
import { DeployConfigPage } from './pages/deployConfigPage';
|
||||||
|
import { DeployPlanPage } from './pages/deployPlanPage';
|
||||||
import { DeployActionPage } from './pages/deployActionPage';
|
import { DeployActionPage } from './pages/deployActionPage';
|
||||||
import { DacFxSummaryPage } from './pages/dacFxSummaryPage';
|
import { DacFxSummaryPage } from './pages/dacFxSummaryPage';
|
||||||
import { ExportConfigPage } from './pages/exportConfigPage';
|
import { ExportConfigPage } from './pages/exportConfigPage';
|
||||||
@@ -38,10 +39,17 @@ export enum Operation {
|
|||||||
export enum DeployOperationPath {
|
export enum DeployOperationPath {
|
||||||
selectOperation,
|
selectOperation,
|
||||||
deployOptions,
|
deployOptions,
|
||||||
|
deployPlan,
|
||||||
deployAction,
|
deployAction,
|
||||||
summary
|
summary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DeployNewOperationPath {
|
||||||
|
selectOperation,
|
||||||
|
deployOptions,
|
||||||
|
summary
|
||||||
|
}
|
||||||
|
|
||||||
export enum ExtractOperationPath {
|
export enum ExtractOperationPath {
|
||||||
selectOperation,
|
selectOperation,
|
||||||
options,
|
options,
|
||||||
@@ -87,6 +95,7 @@ export class DataTierApplicationWizard {
|
|||||||
this.wizard = sqlops.window.modelviewdialog.createWizard('Data-tier Application Wizard');
|
this.wizard = sqlops.window.modelviewdialog.createWizard('Data-tier Application Wizard');
|
||||||
let selectOperationWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.selectOperationPageName', 'Select an Operation'));
|
let selectOperationWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.selectOperationPageName', 'Select an Operation'));
|
||||||
let deployConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployConfigPageName', 'Select Deploy Dacpac Settings'));
|
let deployConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployConfigPageName', 'Select Deploy Dacpac Settings'));
|
||||||
|
let deployPlanWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployPlanPage', 'Review the deploy plan'));
|
||||||
let deployActionWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployActionPageName', 'Select Action'));
|
let deployActionWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployActionPageName', 'Select Action'));
|
||||||
let summaryWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.summaryPageName', 'Summary'));
|
let summaryWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.summaryPageName', 'Summary'));
|
||||||
let extractConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.extractConfigPageName', 'Select Extract Dacpac Settings'));
|
let extractConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.extractConfigPageName', 'Select Extract Dacpac Settings'));
|
||||||
@@ -95,6 +104,7 @@ export class DataTierApplicationWizard {
|
|||||||
|
|
||||||
this.pages.set('selectOperation', new Page(selectOperationWizardPage));
|
this.pages.set('selectOperation', new Page(selectOperationWizardPage));
|
||||||
this.pages.set('deployConfig', new Page(deployConfigWizardPage));
|
this.pages.set('deployConfig', new Page(deployConfigWizardPage));
|
||||||
|
this.pages.set('deployPlan', new Page(deployPlanWizardPage));
|
||||||
this.pages.set('deployAction', new Page(deployActionWizardPage));
|
this.pages.set('deployAction', new Page(deployActionWizardPage));
|
||||||
this.pages.set('extractConfig', new Page(extractConfigWizardPage));
|
this.pages.set('extractConfig', new Page(extractConfigWizardPage));
|
||||||
this.pages.set('importConfig', new Page(importConfigWizardPage));
|
this.pages.set('importConfig', new Page(importConfigWizardPage));
|
||||||
@@ -116,6 +126,12 @@ export class DataTierApplicationWizard {
|
|||||||
await deployConfigDacFxPage.start();
|
await deployConfigDacFxPage.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deployPlanWizardPage.registerContent(async (view) => {
|
||||||
|
let deployPlanDacFxPage = new DeployPlanPage(this, deployPlanWizardPage, this.model, view);
|
||||||
|
this.pages.get('deployPlan').dacFxPage = deployPlanDacFxPage;
|
||||||
|
await deployPlanDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
deployActionWizardPage.registerContent(async (view) => {
|
deployActionWizardPage.registerContent(async (view) => {
|
||||||
let deployActionDacFxPage = new DeployActionPage(this, deployActionWizardPage, this.model, view);
|
let deployActionDacFxPage = new DeployActionPage(this, deployActionWizardPage, this.model, view);
|
||||||
this.pages.get('deployAction').dacFxPage = deployActionDacFxPage;
|
this.pages.get('deployAction').dacFxPage = deployActionDacFxPage;
|
||||||
@@ -166,7 +182,7 @@ export class DataTierApplicationWizard {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.wizard.pages = [selectOperationWizardPage, deployConfigWizardPage, deployActionWizardPage, summaryWizardPage];
|
this.wizard.pages = [selectOperationWizardPage, deployConfigWizardPage, deployPlanWizardPage, deployActionWizardPage, summaryWizardPage];
|
||||||
this.wizard.generateScriptButton.hidden = true;
|
this.wizard.generateScriptButton.hidden = true;
|
||||||
this.wizard.generateScriptButton.onClick(async () => await this.generateDeployScript());
|
this.wizard.generateScriptButton.onClick(async () => await this.generateDeployScript());
|
||||||
this.wizard.doneButton.onClick(async () => await this.executeOperation());
|
this.wizard.doneButton.onClick(async () => await this.executeOperation());
|
||||||
@@ -323,10 +339,12 @@ export class DataTierApplicationWizard {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.deployAction) {
|
|
||||||
page = this.pages.get('deployAction');
|
|
||||||
} else if (this.isSummaryPage(idx)) {
|
} else if (this.isSummaryPage(idx)) {
|
||||||
page = this.pages.get('summary');
|
page = this.pages.get('summary');
|
||||||
|
} else if ((this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.deployPlan) {
|
||||||
|
page = this.pages.get('deployPlan');
|
||||||
|
} else if ((this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.deployAction) {
|
||||||
|
page = this.pages.get('deployAction');
|
||||||
}
|
}
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
@@ -336,9 +354,24 @@ export class DataTierApplicationWizard {
|
|||||||
return this.selectedOperation === Operation.import && idx === ImportOperationPath.summary
|
return this.selectedOperation === Operation.import && idx === ImportOperationPath.summary
|
||||||
|| this.selectedOperation === Operation.export && idx === ExportOperationPath.summary
|
|| this.selectedOperation === Operation.export && idx === ExportOperationPath.summary
|
||||||
|| this.selectedOperation === Operation.extract && idx === ExtractOperationPath.summary
|
|| this.selectedOperation === Operation.extract && idx === ExtractOperationPath.summary
|
||||||
|
|| this.selectedOperation === Operation.deploy && !this.model.upgradeExisting && idx === DeployNewOperationPath.summary
|
||||||
|| (this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.summary;
|
|| (this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async generateDeployPlan(): Promise<string> {
|
||||||
|
let service = await DataTierApplicationWizard.getService(this.model.server.providerName);
|
||||||
|
let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
|
||||||
|
let result = await service.generateDeployPlan(this.model.filePath, this.model.database, ownerUri, sqlops.TaskExecutionMode.execute);
|
||||||
|
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('alertData.deployPlanErrorMessage', "Generating deploy plan failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.report;
|
||||||
|
}
|
||||||
|
|
||||||
private static async getService(providerName: string): Promise<sqlops.DacFxServicesProvider> {
|
private static async getService(providerName: string): Promise<sqlops.DacFxServicesProvider> {
|
||||||
let service = sqlops.dataprotocol.getProvider<sqlops.DacFxServicesProvider>(providerName, sqlops.DataProviderType.DacFxServicesProvider);
|
let service = sqlops.dataprotocol.getProvider<sqlops.DacFxServicesProvider>(providerName, sqlops.DataProviderType.DacFxServicesProvider);
|
||||||
return service;
|
return service;
|
||||||
|
|||||||
@@ -131,7 +131,15 @@ export class DacFxSummaryPage extends BasePage {
|
|||||||
|
|
||||||
this.table.updateProperties({
|
this.table.updateProperties({
|
||||||
data: data,
|
data: data,
|
||||||
columns: ['Setting', 'Value'],
|
columns: [
|
||||||
|
{
|
||||||
|
value: localize('dacfx.settingColumn', 'Setting'),
|
||||||
|
cssClass: 'align-with-header'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: localize('dacfx.valueColumn', 'Value'),
|
||||||
|
cssClass: 'align-with-header'
|
||||||
|
}],
|
||||||
width: 700,
|
width: 700,
|
||||||
height: 200
|
height: 200
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export class DeployActionPage extends DacFxConfigPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onPageEnter(): Promise<boolean> {
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
// generate script file path in case the database changed since last time the page was entered
|
||||||
|
this.setDefaultScriptFilePath();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,11 +122,7 @@ export class DeployActionPage extends DacFxConfigPage {
|
|||||||
this.createFileBrowserParts();
|
this.createFileBrowserParts();
|
||||||
|
|
||||||
//default filepath
|
//default filepath
|
||||||
let now = new Date();
|
this.setDefaultScriptFilePath();
|
||||||
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;
|
|
||||||
|
|
||||||
this.fileButton.onDidClick(async (click) => {
|
this.fileButton.onDidClick(async (click) => {
|
||||||
let fileUri = await vscode.window.showSaveDialog(
|
let fileUri = await vscode.window.showSaveDialog(
|
||||||
{
|
{
|
||||||
@@ -151,15 +149,15 @@ export class DeployActionPage extends DacFxConfigPage {
|
|||||||
return {
|
return {
|
||||||
title: '',
|
title: '',
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
title: localize('dacfx.generatedScriptLocation','Deployment Script Location'),
|
title: localize('dacfx.generatedScriptLocation', 'Deployment Script Location'),
|
||||||
component: this.fileTextBox,
|
component: this.fileTextBox,
|
||||||
layout: {
|
layout: {
|
||||||
horizontal: true,
|
horizontal: true,
|
||||||
componentWidth: 400
|
componentWidth: 400
|
||||||
},
|
},
|
||||||
actions: [this.fileButton]
|
actions: [this.fileButton]
|
||||||
},],
|
},],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +166,13 @@ export class DeployActionPage extends DacFxConfigPage {
|
|||||||
this.fileButton.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() {
|
public setupNavigationValidator() {
|
||||||
this.instance.registerNavigationValidator(() => {
|
this.instance.registerNavigationValidator(() => {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import * as vscode from 'vscode';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { DacFxDataModel } from '../api/models';
|
import { DacFxDataModel } from '../api/models';
|
||||||
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
import { DataTierApplicationWizard, DeployOperationPath, Operation } from '../dataTierApplicationWizard';
|
||||||
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -122,6 +122,12 @@ export class DeployConfigPage extends DacFxConfigPage {
|
|||||||
this.formBuilder.removeFormItem(this.databaseComponent);
|
this.formBuilder.removeFormItem(this.databaseComponent);
|
||||||
this.formBuilder.addFormItem(this.databaseDropdownComponent, { horizontal: true, componentWidth: 400 });
|
this.formBuilder.addFormItem(this.databaseDropdownComponent, { horizontal: true, componentWidth: 400 });
|
||||||
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
this.model.database = (<sqlops.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(() => {
|
newRadioButton.onDidClick(() => {
|
||||||
@@ -129,6 +135,11 @@ export class DeployConfigPage extends DacFxConfigPage {
|
|||||||
this.formBuilder.removeFormItem(this.databaseDropdownComponent);
|
this.formBuilder.removeFormItem(this.databaseDropdownComponent);
|
||||||
this.formBuilder.addFormItem(this.databaseComponent, { horizontal: true, componentWidth: 400 });
|
this.formBuilder.addFormItem(this.databaseComponent, { horizontal: true, componentWidth: 400 });
|
||||||
this.model.database = this.databaseTextBox.value;
|
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
|
//Initialize with upgrade existing true
|
||||||
|
|||||||
296
extensions/import/src/wizard/pages/deployPlanPage.ts
Normal file
296
extensions/import/src/wizard/pages/deployPlanPage.ts
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
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: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
private formBuilder: sqlops.FormBuilder;
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
private table: sqlops.TableComponent;
|
||||||
|
private loader: sqlops.LoadingComponent;
|
||||||
|
private dataLossCheckbox: sqlops.CheckBoxComponent;
|
||||||
|
private dataLossText: sqlops.TextComponent;
|
||||||
|
private dataLossComponentGroup: sqlops.FormComponentGroup;
|
||||||
|
private noDataLossTextComponent: sqlops.FormComponent;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.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<sqlops.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<sqlops.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<sqlops.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): sqlops.TableColumn[] {
|
||||||
|
let columns: sqlops.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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DacFxDataModel } from '../api/models';
|
import { DacFxDataModel } from '../api/models';
|
||||||
import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWizard';
|
import { DataTierApplicationWizard, Operation, DeployOperationPath, ExtractOperationPath, ImportOperationPath, ExportOperationPath } from '../dataTierApplicationWizard';
|
||||||
import { BasePage } from '../api/basePage';
|
import { BasePage } from '../api/basePage';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -73,10 +73,12 @@ export class SelectOperationPage extends BasePage {
|
|||||||
|
|
||||||
//add deploy pages
|
//add deploy pages
|
||||||
let configPage = this.instance.pages.get('deployConfig');
|
let configPage = this.instance.pages.get('deployConfig');
|
||||||
this.instance.wizard.addPage(configPage.wizardPage, 1);
|
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');
|
let actionPage = this.instance.pages.get('deployAction');
|
||||||
this.instance.wizard.addPage(actionPage.wizardPage, 2);
|
this.instance.wizard.addPage(actionPage.wizardPage, DeployOperationPath.deployAction);
|
||||||
this.addSummaryPage(3);
|
this.addSummaryPage(DeployOperationPath.summary);
|
||||||
|
|
||||||
// change button text and operation
|
// change button text and operation
|
||||||
this.instance.setDoneButton(Operation.deploy);
|
this.instance.setDoneButton(Operation.deploy);
|
||||||
@@ -100,8 +102,8 @@ export class SelectOperationPage extends BasePage {
|
|||||||
|
|
||||||
// add the extract page
|
// add the extract page
|
||||||
let page = this.instance.pages.get('extractConfig');
|
let page = this.instance.pages.get('extractConfig');
|
||||||
this.instance.wizard.addPage(page.wizardPage, 1);
|
this.instance.wizard.addPage(page.wizardPage, ExtractOperationPath.options);
|
||||||
this.addSummaryPage(2);
|
this.addSummaryPage(ExtractOperationPath.summary);
|
||||||
|
|
||||||
// change button text and operation
|
// change button text and operation
|
||||||
this.instance.setDoneButton(Operation.extract);
|
this.instance.setDoneButton(Operation.extract);
|
||||||
@@ -125,8 +127,8 @@ export class SelectOperationPage extends BasePage {
|
|||||||
|
|
||||||
// add the import page
|
// add the import page
|
||||||
let page = this.instance.pages.get('importConfig');
|
let page = this.instance.pages.get('importConfig');
|
||||||
this.instance.wizard.addPage(page.wizardPage, 1);
|
this.instance.wizard.addPage(page.wizardPage, ImportOperationPath.options);
|
||||||
this.addSummaryPage(2);
|
this.addSummaryPage(ImportOperationPath.summary);
|
||||||
|
|
||||||
// change button text and operation
|
// change button text and operation
|
||||||
this.instance.setDoneButton(Operation.import);
|
this.instance.setDoneButton(Operation.import);
|
||||||
@@ -150,8 +152,8 @@ export class SelectOperationPage extends BasePage {
|
|||||||
|
|
||||||
// add the export pages
|
// add the export pages
|
||||||
let page = this.instance.pages.get('exportConfig');
|
let page = this.instance.pages.get('exportConfig');
|
||||||
this.instance.wizard.addPage(page.wizardPage, 1);
|
this.instance.wizard.addPage(page.wizardPage, ExportOperationPath.options);
|
||||||
this.addSummaryPage(2);
|
this.addSummaryPage(ExportOperationPath.summary);
|
||||||
|
|
||||||
// change button text and operation
|
// change button text and operation
|
||||||
this.instance.setDoneButton(Operation.export);
|
this.instance.setDoneButton(Operation.export);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.5.0-alpha.70",
|
"version": "1.5.0-alpha.71",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||||
|
|||||||
@@ -339,6 +339,13 @@ export interface GenerateDeployScriptParams {
|
|||||||
taskExecutionMode: TaskExecutionMode;
|
taskExecutionMode: TaskExecutionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GenerateDeployPlanParams {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
export namespace ExportRequest {
|
export namespace ExportRequest {
|
||||||
export const type = new RequestType<ExportParams, sqlops.DacFxResult, void, void>('dacfx/export');
|
export const type = new RequestType<ExportParams, sqlops.DacFxResult, void, void>('dacfx/export');
|
||||||
}
|
}
|
||||||
@@ -359,4 +366,7 @@ export namespace GenerateDeployScriptRequest {
|
|||||||
export const type = new RequestType<GenerateDeployScriptParams, sqlops.DacFxResult, void, void>('dacfx/generateDeploymentScript');
|
export const type = new RequestType<GenerateDeployScriptParams, sqlops.DacFxResult, void, void>('dacfx/generateDeploymentScript');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace GenerateDeployPlanRequest {
|
||||||
|
export const type = new RequestType<GenerateDeployPlanParams, sqlops.GenerateDeployPlanResult, void, void>('dacfx/generateDeployPlan');
|
||||||
|
}
|
||||||
// ------------------------------- < DacFx > ------------------------------------
|
// ------------------------------- < DacFx > ------------------------------------
|
||||||
@@ -113,7 +113,20 @@ export class DacFxServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
return r;
|
return r;
|
||||||
},
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(contracts.DeployRequest.type, e);
|
client.logFailedRequest(contracts.GenerateDeployScriptRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let generateDeployPlan = (packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.GenerateDeployPlanResult> => {
|
||||||
|
let params: contracts.GenerateDeployPlanParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
|
||||||
|
return client.sendRequest(contracts.GenerateDeployPlanRequest.type, params).then(
|
||||||
|
r => {
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(contracts.GenerateDeployPlanRequest.type, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -125,7 +138,8 @@ export class DacFxServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
importBacpac,
|
importBacpac,
|
||||||
extractDacpac,
|
extractDacpac,
|
||||||
deployDacpac,
|
deployDacpac,
|
||||||
generateDeployScript
|
generateDeployScript,
|
||||||
|
generateDeployPlan
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface IDacFxService {
|
|||||||
extractDacpac(sourceDatabaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
extractDacpac(sourceDatabaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
generateDeployScript(packageFilePath: string, targetDatabaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
generateDeployScript(packageFilePath: string, targetDatabaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
|
generateDeployPlan(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DacFxService implements IDacFxService {
|
export class DacFxService implements IDacFxService {
|
||||||
@@ -68,6 +69,12 @@ export class DacFxService implements IDacFxService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.GenerateDeployPlanResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.generateDeployPlan(packageFilePath, databaseName, ownerUri, taskExecutionMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _runAction<T>(uri: string, action: (handler: sqlops.DacFxServicesProvider) => Thenable<T>): Thenable<T> {
|
private _runAction<T>(uri: string, action: (handler: sqlops.DacFxServicesProvider) => Thenable<T>): Thenable<T> {
|
||||||
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
|
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
|
||||||
|
|
||||||
|
|||||||
12
src/sql/sqlops.d.ts
vendored
12
src/sql/sqlops.d.ts
vendored
@@ -1634,6 +1634,10 @@ declare module 'sqlops' {
|
|||||||
operationId: string;
|
operationId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GenerateDeployPlanResult extends DacFxResult {
|
||||||
|
report: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExportParams {
|
export interface ExportParams {
|
||||||
databaseName: string;
|
databaseName: string;
|
||||||
packageFilePath: string;
|
packageFilePath: string;
|
||||||
@@ -1673,12 +1677,20 @@ declare module 'sqlops' {
|
|||||||
taskExecutionMode: TaskExecutionMode;
|
taskExecutionMode: TaskExecutionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GenerateDeployPlan {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DacFxServicesProvider extends DataProvider {
|
export interface DacFxServicesProvider extends DataProvider {
|
||||||
exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
|
generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<GenerateDeployPlanResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security service interfaces ------------------------------------------------------------------------
|
// Security service interfaces ------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -445,6 +445,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
},
|
},
|
||||||
generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
return self._proxy.$generateDeployScript(handle, packageFilePath, databaseName, scriptFilePath, ownerUri, taskExecutionMode);
|
return self._proxy.$generateDeployScript(handle, packageFilePath, databaseName, scriptFilePath, ownerUri, taskExecutionMode);
|
||||||
|
},
|
||||||
|
generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.GenerateDeployPlanResult> {
|
||||||
|
return self._proxy.$generateDeployPlan(handle, packageFilePath, databaseName, ownerUri, taskExecutionMode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -450,6 +450,11 @@ export abstract class ExtHostDataProtocolShape {
|
|||||||
*/
|
*/
|
||||||
$generateDeployScript(handle: number, packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
$generateDeployScript(handle: number, packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DacFx generate deploy plan
|
||||||
|
*/
|
||||||
|
$generateDeployPlan(handle: number, packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.GenerateDeployPlanResult> { throw ni(); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user