diff --git a/extensions/import/src/wizard/api/models.ts b/extensions/import/src/wizard/api/models.ts index 12b305dcb7..3961f1e0ec 100644 --- a/extensions/import/src/wizard/api/models.ts +++ b/extensions/import/src/wizard/api/models.ts @@ -45,4 +45,6 @@ export interface DacFxDataModel extends BaseDataModel { filePath: string; version: string; upgradeExisting: boolean; + scriptFilePath: string; + generateScriptAndDeploy: boolean; } diff --git a/extensions/import/src/wizard/dataTierApplicationWizard.ts b/extensions/import/src/wizard/dataTierApplicationWizard.ts index e91ff59381..3c1103e9b5 100644 --- a/extensions/import/src/wizard/dataTierApplicationWizard.ts +++ b/extensions/import/src/wizard/dataTierApplicationWizard.ts @@ -8,6 +8,7 @@ import * as nls from 'vscode-nls'; import * as sqlops from 'sqlops'; import { SelectOperationPage } from './pages/selectOperationpage'; import { DeployConfigPage } from './pages/deployConfigPage'; +import { DeployActionPage } from './pages/deployActionPage'; import { DacFxSummaryPage } from './pages/dacFxSummaryPage'; import { ExportConfigPage } from './pages/exportConfigPage'; import { ExtractConfigPage } from './pages/extractConfigPage'; @@ -30,7 +31,33 @@ export enum Operation { deploy, extract, import, - export + export, + generateDeployScript +} + +export enum DeployOperationPath { + selectOperation, + deployOptions, + deployAction, + summary +} + +export enum ExtractOperationPath { + selectOperation, + options, + summary +} + +export enum ImportOperationPath { + selectOperation, + options, + summary +} + +export enum ExportOperationPath { + selectOperation, + options, + summary } export class DataTierApplicationWizard { @@ -60,6 +87,7 @@ export class DataTierApplicationWizard { this.wizard = sqlops.window.modelviewdialog.createWizard('Data-tier Application Wizard'); 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 deployActionWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployActionPageName', 'Select Action')); let summaryWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.summaryPageName', 'Summary')); let extractConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.extractConfigPageName', 'Select Extract Dacpac Settings')); let importConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.importConfigPageName', 'Select Import Bacpac Settings')); @@ -67,6 +95,7 @@ export class DataTierApplicationWizard { this.pages.set('selectOperation', new Page(selectOperationWizardPage)); this.pages.set('deployConfig', new Page(deployConfigWizardPage)); + this.pages.set('deployAction', new Page(deployActionWizardPage)); this.pages.set('extractConfig', new Page(extractConfigWizardPage)); this.pages.set('importConfig', new Page(importConfigWizardPage)); this.pages.set('exportConfig', new Page(exportConfigWizardPage)); @@ -87,6 +116,12 @@ export class DataTierApplicationWizard { await deployConfigDacFxPage.start(); }); + deployActionWizardPage.registerContent(async (view) => { + let deployActionDacFxPage = new DeployActionPage(this, deployActionWizardPage, this.model, view); + this.pages.get('deployAction').dacFxPage = deployActionDacFxPage; + await deployActionDacFxPage.start(); + }); + extractConfigWizardPage.registerContent(async (view) => { let extractConfigDacFxPage = new ExtractConfigPage(this, extractConfigWizardPage, this.model, view); this.pages.get('extractConfig').dacFxPage = extractConfigDacFxPage; @@ -113,39 +148,27 @@ export class DataTierApplicationWizard { this.wizard.onPageChanged(async (event) => { let idx = event.newPage; - let page: Page; - - if (idx === 1) { - switch (this.selectedOperation) { - case Operation.deploy: { - page = this.pages.get('deployConfig'); - break; - } - case Operation.extract: { - page = this.pages.get('extractConfig'); - break; - } - case Operation.import: { - page = this.pages.get('importConfig'); - break; - } - case Operation.export: { - page = this.pages.get('exportConfig'); - break; - } - } - } else if (idx === 2) { - page = this.pages.get('summary'); - } + let page = this.getPage(idx); if (page !== undefined) { page.dacFxPage.setupNavigationValidator(); page.dacFxPage.onPageEnter(); } + + //do onPageLeave for summary page so that GenerateScript button only shows up if upgrading database + let idxLast = event.lastPage; + + if (this.isSummaryPage(idxLast)) { + let lastPage = this.pages.get('summary'); + if (lastPage) { + lastPage.dacFxPage.onPageLeave(); + } + } }); - this.wizard.pages = [selectOperationWizardPage, deployConfigWizardPage, summaryWizardPage]; + this.wizard.pages = [selectOperationWizardPage, deployConfigWizardPage, deployActionWizardPage, summaryWizardPage]; this.wizard.generateScriptButton.hidden = true; + this.wizard.generateScriptButton.onClick(async () => await this.generateDeployScript()); this.wizard.doneButton.onClick(async () => await this.executeOperation()); this.wizard.open(); @@ -177,6 +200,15 @@ export class DataTierApplicationWizard { this.selectedOperation = Operation.export; break; } + case Operation.generateDeployScript: { + this.wizard.doneButton.label = localize('dacFx.generateScriptButton', 'Generate Script'); + this.selectedOperation = Operation.generateDeployScript; + break; + } + } + + if (operation !== Operation.deploy && operation !== Operation.generateDeployScript) { + this.model.upgradeExisting = false; } } @@ -198,6 +230,10 @@ export class DataTierApplicationWizard { await this.export(); break; } + case Operation.generateDeployScript: { + await this.generateDeployScript(); + break; + } } } @@ -245,6 +281,64 @@ export class DataTierApplicationWizard { } } + private async generateDeployScript() { + if (!this.model.scriptFilePath) { + return; + } + + let service = await DataTierApplicationWizard.getService(this.model.server.providerName); + let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId); + this.wizard.message = { + text: localize('dacfx.scriptGeneratingMessage', 'You can view the status of script generation in the Task History once the wizard is closed'), + level: sqlops.window.modelviewdialog.MessageLevel.Information, + description: '' + }; + + let result = await service.generateDeployScript(this.model.filePath, this.model.database, this.model.scriptFilePath, ownerUri, sqlops.TaskExecutionMode.execute); + if (!result || !result.success) { + vscode.window.showErrorMessage( + localize('alertData.deployErrorMessage', "Deploy failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown')); + } + } + + private getPage(idx: number): Page { + let page: Page; + + if (idx === 1) { + switch (this.selectedOperation) { + case Operation.deploy: { + page = this.pages.get('deployConfig'); + break; + } + case Operation.extract: { + page = this.pages.get('extractConfig'); + break; + } + case Operation.import: { + page = this.pages.get('importConfig'); + break; + } + case Operation.export: { + page = this.pages.get('exportConfig'); + break; + } + } + } else if ((this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.deployAction) { + page = this.pages.get('deployAction'); + } else if (this.isSummaryPage(idx)) { + page = this.pages.get('summary'); + } + + return page; + } + + private isSummaryPage(idx: number): boolean { + return this.selectedOperation === Operation.import && idx === ImportOperationPath.summary + || this.selectedOperation === Operation.export && idx === ExportOperationPath.summary + || this.selectedOperation === Operation.extract && idx === ExtractOperationPath.summary + || (this.selectedOperation === Operation.deploy || this.selectedOperation === Operation.generateDeployScript) && idx === DeployOperationPath.summary; + } + private static async getService(providerName: string): Promise { let service = sqlops.dataprotocol.getProvider(providerName, sqlops.DataProviderType.DacFxServicesProvider); return service; diff --git a/extensions/import/src/wizard/pages/dacFxSummaryPage.ts b/extensions/import/src/wizard/pages/dacFxSummaryPage.ts index 0318f94c21..4224bc1507 100644 --- a/extensions/import/src/wizard/pages/dacFxSummaryPage.ts +++ b/extensions/import/src/wizard/pages/dacFxSummaryPage.ts @@ -49,6 +49,14 @@ export class DacFxSummaryPage extends BasePage { async onPageEnter(): Promise { this.populateTable(); this.loader.loading = false; + if (this.model.upgradeExisting && this.model.generateScriptAndDeploy) { + this.instance.wizard.generateScriptButton.hidden = false; + } + return true; + } + + async onPageLeave(): Promise { + this.instance.wizard.generateScriptButton.hidden = true; return true; } @@ -68,6 +76,10 @@ export class DacFxSummaryPage extends BasePage { 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: { @@ -75,6 +87,13 @@ export class DacFxSummaryPage extends BasePage { [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: { @@ -99,12 +118,21 @@ export class DacFxSummaryPage extends BasePage { [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: ['Setting', 'Value'], - width: 600, + width: 700, height: 200 }); } diff --git a/extensions/import/src/wizard/pages/deployActionPage.ts b/extensions/import/src/wizard/pages/deployActionPage.ts new file mode 100644 index 0000000000..ceb769dfac --- /dev/null +++ b/extensions/import/src/wizard/pages/deployActionPage.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * 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 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: sqlops.window.modelviewdialog.WizardPage; + protected readonly instance: DataTierApplicationWizard; + protected readonly model: DacFxDataModel; + protected readonly view: sqlops.ModelView; + private deployRadioButton: sqlops.RadioButtonComponent; + private deployScriptRadioButton: sqlops.RadioButtonComponent; + private scriptRadioButton: sqlops.RadioButtonComponent; + private form: sqlops.FormContainer; + + public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) { + super(instance, wizardPage, model, view); + } + + async start(): Promise { + 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 { + return true; + } + + private async createDeployRadioButton(): Promise { + 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 { + 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 { + 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 { + this.createFileBrowserParts(); + + //default filepath + 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; + + 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; + } + + public setupNavigationValidator() { + this.instance.registerNavigationValidator(() => { + return true; + }); + } +} diff --git a/extensions/import/src/wizard/pages/selectOperationpage.ts b/extensions/import/src/wizard/pages/selectOperationpage.ts index 5c240f8673..64bbb7af7f 100644 --- a/extensions/import/src/wizard/pages/selectOperationpage.ts +++ b/extensions/import/src/wizard/pages/selectOperationpage.ts @@ -58,11 +58,6 @@ export class SelectOperationPage extends BasePage { } async onPageEnter(): Promise { - let numPages = this.instance.wizard.pages.length; - for (let i = numPages - 1; i > 2; --i) { - await this.instance.wizard.removePage(i); - } - return true; } @@ -74,12 +69,14 @@ export class SelectOperationPage extends BasePage { }).component(); this.deployRadioButton.onDidClick(() => { - // remove the previous page - this.instance.wizard.removePage(1); + this.removePages(); - // add deploy page - let page = this.instance.pages.get('deployConfig'); - this.instance.wizard.addPage(page.wizardPage, 1); + //add deploy pages + let configPage = this.instance.pages.get('deployConfig'); + this.instance.wizard.addPage(configPage.wizardPage, 1); + let actionPage = this.instance.pages.get('deployAction'); + this.instance.wizard.addPage(actionPage.wizardPage, 2); + this.addSummaryPage(3); // change button text and operation this.instance.setDoneButton(Operation.deploy); @@ -99,12 +96,12 @@ export class SelectOperationPage extends BasePage { }).component(); this.extractRadioButton.onDidClick(() => { - // remove the previous pages - this.instance.wizard.removePage(1); + this.removePages(); // add the extract page let page = this.instance.pages.get('extractConfig'); this.instance.wizard.addPage(page.wizardPage, 1); + this.addSummaryPage(2); // change button text and operation this.instance.setDoneButton(Operation.extract); @@ -124,12 +121,12 @@ export class SelectOperationPage extends BasePage { }).component(); this.importRadioButton.onDidClick(() => { - // remove the previous page - this.instance.wizard.removePage(1); + this.removePages(); // add the import page let page = this.instance.pages.get('importConfig'); this.instance.wizard.addPage(page.wizardPage, 1); + this.addSummaryPage(2); // change button text and operation this.instance.setDoneButton(Operation.import); @@ -149,12 +146,12 @@ export class SelectOperationPage extends BasePage { }).component(); this.exportRadioButton.onDidClick(() => { - // remove the 2 previous pages - this.instance.wizard.removePage(1); + this.removePages(); // add the export pages let page = this.instance.pages.get('exportConfig'); this.instance.wizard.addPage(page.wizardPage, 1); + this.addSummaryPage(2); // change button text and operation this.instance.setDoneButton(Operation.export); @@ -166,6 +163,18 @@ export class SelectOperationPage extends BasePage { }; } + 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; diff --git a/extensions/mssql/src/config.json b/extensions/mssql/src/config.json index a1c4ad3185..c75424c40e 100644 --- a/extensions/mssql/src/config.json +++ b/extensions/mssql/src/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "1.5.0-alpha.67", + "version": "1.5.0-alpha.68", "downloadFileNames": { "Windows_86": "win-x86-netcoreapp2.2.zip", "Windows_64": "win-x64-netcoreapp2.2.zip", diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 9d17ad48cc..7411aa8878 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -331,6 +331,14 @@ export interface DeployParams { taskExecutionMode: TaskExecutionMode; } +export interface GenerateDeployScriptParams { + packageFilePath: string; + databaseName: string; + scriptFilePath: string; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; +} + export namespace ExportRequest { export const type = new RequestType('dacfx/export'); } @@ -347,4 +355,8 @@ export namespace DeployRequest { export const type = new RequestType('dacfx/deploy'); } +export namespace GenerateDeployScriptRequest { + export const type = new RequestType('dacfx/generateDeploymentScript'); +} + // ------------------------------- < DacFx > ------------------------------------ \ No newline at end of file diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index cdbd5783a1..35673c671f 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -106,12 +106,26 @@ export class DacFxServicesFeature extends SqlOpsFeature { ); }; + let generateDeployScript = (packageFilePath: string, targetDatabaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable => { + let params: contracts.GenerateDeployScriptParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, scriptFilePath: scriptFilePath, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode }; + return client.sendRequest(contracts.GenerateDeployScriptRequest.type, params).then( + r => { + return r; + }, + e => { + client.logFailedRequest(contracts.DeployRequest.type, e); + return Promise.resolve(undefined); + } + ); + }; + return sqlops.dataprotocol.registerDacFxServicesProvider({ providerId: client.providerId, exportBacpac, importBacpac, extractDacpac, - deployDacpac + deployDacpac, + generateDeployScript }); } } diff --git a/src/sql/platform/dacfx/common/dacFxService.ts b/src/sql/platform/dacfx/common/dacFxService.ts index c9277f301b..c27f94ca59 100644 --- a/src/sql/platform/dacfx/common/dacFxService.ts +++ b/src/sql/platform/dacfx/common/dacFxService.ts @@ -22,6 +22,7 @@ export interface IDacFxService { importBacpac(packageFilePath: string, targetDatabaseName: 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; + generateDeployScript(packageFilePath: string, targetDatabaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void; } export class DacFxService implements IDacFxService { @@ -61,6 +62,12 @@ export class DacFxService implements IDacFxService { }); } + generateDeployScript(packageFilePath: string, databaseName: string, generateDeployScript: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable { + return this._runAction(ownerUri, (runner) => { + return runner.generateDeployScript(packageFilePath, databaseName, generateDeployScript, ownerUri, taskExecutionMode); + }); + } + private _runAction(uri: string, action: (handler: sqlops.DacFxServicesProvider) => Thenable): Thenable { let providerId: string = this._connectionService.getProviderIdFromUri(uri); diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index 8964aca9c5..f0dec6d5c3 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1626,11 +1626,20 @@ declare module 'sqlops' { taskExecutionMode: TaskExecutionMode; } + export interface GenerateDeployScriptParams { + packageFilePath: string; + databaseName: string; + scriptFilePath: string; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + export interface DacFxServicesProvider extends DataProvider { exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; + generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; } // Security service interfaces ------------------------------------------------------------------------ diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts index 21830fe49e..ffa34f79d2 100644 --- a/src/sql/workbench/api/node/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -415,6 +415,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { }, deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable { return self._proxy.$deployDacpac(handle, packageFilePath, databaseName, upgradeExisting, ownerUri, taskExecutionMode); + }, + generateDeployScript(packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable { + return self._proxy.$generateDeployScript(handle, packageFilePath, databaseName, scriptFilePath, ownerUri, taskExecutionMode); } }); diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 2414ae3f71..3069487015 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -440,6 +440,12 @@ export abstract class ExtHostDataProtocolShape { * DacFx deploy dacpac */ $deployDacpac(handle: number, packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable { throw ni(); } + + /** + * DacFx generate deploy script + */ + $generateDeployScript(handle: number, packageFilePath: string, databaseName: string, scriptFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable { throw ni(); } + } /**