diff --git a/extensions/mssql/config.json b/extensions/mssql/config.json index a07208ea6e..a5934d5d30 100644 --- a/extensions/mssql/config.json +++ b/extensions/mssql/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "3.0.0-release.174", + "version": "3.0.0-release.178", "downloadFileNames": { "Windows_86": "win-x86-net5.0.zip", "Windows_64": "win-x64-net5.0.zip", diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 773d6b9d35..0b8f9d2335 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1044,27 +1044,28 @@ export namespace GetSqlMigrationAssessmentItemsRequest { export interface TableDesignerEditRequestParams { tableInfo: azdata.designers.TableInfo, - tableChangeInfo: azdata.designers.DesignerEdit, - viewModel: azdata.designers.DesignerViewModel + tableChangeInfo: azdata.designers.DesignerEdit } -export interface SaveTableDesignerChangesRequestParams { - tableInfo: azdata.designers.TableInfo, - viewModel: azdata.designers.DesignerViewModel -} - -export namespace GetTableDesignerInfoRequest { - export const type = new RequestType('tabledesigner/gettabledesignerinfo'); +export namespace InitializeTableDesignerRequest { + export const type = new RequestType('tabledesigner/initialize'); } export namespace ProcessTableDesignerEditRequest { export const type = new RequestType('tabledesigner/processedit'); } -export namespace SaveTableDesignerChangesRequest { - export const type = new RequestType('tabledesigner/savechanges'); +export namespace PublishTableDesignerChangesRequest { + export const type = new RequestType('tabledesigner/save'); } +export namespace TableDesignerGenerateScriptRequest { + export const type = new RequestType('tabledesigner/script'); +} + +export namespace TableDesignerGenerateChangePreviewReportRequest { + export const type = new RequestType('tabledesigner/report'); +} export namespace DisposeTableDesignerRequest { export const type = new RequestType('tabledesigner/dispose'); } diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 812df28c23..f4f6e8c896 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -1108,19 +1108,18 @@ export class TableDesignerFeature extends SqlOpsFeature { protected registerProvider(options: undefined): Disposable { const client = this._client; - const getTableDesignerInfo = (tableInfo: azdata.designers.TableInfo): Thenable => { + const initializeTableDesigner = (tableInfo: azdata.designers.TableInfo): Thenable => { try { - return client.sendRequest(contracts.GetTableDesignerInfoRequest.type, tableInfo); + return client.sendRequest(contracts.InitializeTableDesignerRequest.type, tableInfo); } catch (e) { - client.logFailedRequest(contracts.GetTableDesignerInfoRequest.type, e); + client.logFailedRequest(contracts.InitializeTableDesignerRequest.type, e); return Promise.reject(e); } }; - const processTableEdit = (tableInfo: azdata.designers.TableInfo, viewModel: azdata.designers.DesignerViewModel, tableChangeInfo: azdata.designers.DesignerEdit): Thenable => { + const processTableEdit = (tableInfo: azdata.designers.TableInfo, tableChangeInfo: azdata.designers.DesignerEdit): Thenable => { let params: contracts.TableDesignerEditRequestParams = { tableInfo: tableInfo, - viewModel: viewModel, tableChangeInfo: tableChangeInfo }; try { @@ -1132,16 +1131,32 @@ export class TableDesignerFeature extends SqlOpsFeature { } }; - const saveTable = (tableInfo: azdata.designers.TableInfo, viewModel: azdata.designers.DesignerViewModel): Thenable => { - let params: contracts.SaveTableDesignerChangesRequestParams = { - tableInfo: tableInfo, - viewModel: viewModel - }; + const publishChanges = (tableInfo: azdata.designers.TableInfo): Thenable => { try { - return client.sendRequest(contracts.SaveTableDesignerChangesRequest.type, params); + return client.sendRequest(contracts.PublishTableDesignerChangesRequest.type, tableInfo); } catch (e) { - client.logFailedRequest(contracts.SaveTableDesignerChangesRequest.type, e); + client.logFailedRequest(contracts.PublishTableDesignerChangesRequest.type, e); + return Promise.reject(e); + } + }; + + const generateScript = (tableInfo: azdata.designers.TableInfo): Thenable => { + try { + return client.sendRequest(contracts.TableDesignerGenerateScriptRequest.type, tableInfo); + } + catch (e) { + client.logFailedRequest(contracts.TableDesignerGenerateScriptRequest.type, e); + return Promise.reject(e); + } + }; + + const generatePreviewReport = (tableInfo: azdata.designers.TableInfo): Thenable => { + try { + return client.sendRequest(contracts.TableDesignerGenerateChangePreviewReportRequest.type, tableInfo); + } + catch (e) { + client.logFailedRequest(contracts.TableDesignerGenerateChangePreviewReportRequest.type, e); return Promise.reject(e); } }; @@ -1158,9 +1173,11 @@ export class TableDesignerFeature extends SqlOpsFeature { return azdata.dataprotocol.registerTableDesignerProvider({ providerId: client.providerId, - getTableDesignerInfo, + initializeTableDesigner, processTableEdit, - saveTable, + publishChanges, + generateScript, + generatePreviewReport, disposeTableDesigner }); } diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 1c70602651..5d37e2fc29 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1013,24 +1013,35 @@ declare module 'azdata' { */ export interface TableDesignerProvider extends DataProvider { /** - * Gets the table designer information for the specified table. + * Initialize the table designer for the specified table. * @param table the table information. */ - getTableDesignerInfo(table: TableInfo): Thenable; + initializeTableDesigner(table: TableInfo): Thenable; + /** * Process the table change. * @param table the table information - * @param viewModel the object contains the state of the table designer * @param tableChangeInfo the information about the change user made through the UI. */ - processTableEdit(table: TableInfo, viewModel: DesignerViewModel, tableChangeInfo: DesignerEdit): Thenable; + processTableEdit(table: TableInfo, tableChangeInfo: DesignerEdit): Thenable; /** - * Save the table + * Publish the changes. * @param table the table information - * @param viewModel the object contains the state of the table designer */ - saveTable(table: TableInfo, viewModel: DesignerViewModel): Thenable; + publishChanges(table: TableInfo): Thenable; + + /** + * Generate script for the changes. + * @param table the table information + */ + generateScript(table: TableInfo): Thenable; + + /** + * Generate preview report describing the changes to be made. + * @param table the table information + */ + generatePreviewReport(table: TableInfo): Thenable; /** * Notify the provider that the table designer has been closed. diff --git a/src/sql/platform/telemetry/common/telemetryKeys.ts b/src/sql/platform/telemetry/common/telemetryKeys.ts index aef90496d0..bc00eb699c 100644 --- a/src/sql/platform/telemetry/common/telemetryKeys.ts +++ b/src/sql/platform/telemetry/common/telemetryKeys.ts @@ -26,7 +26,8 @@ export const enum ModalDialogName { AutoOAuth = 'AutoOAuth', AddNewDashboardTab = 'AddNewDashboardTab', ProfilerFilter = 'ProfilerFilter', - CalloutDialog = 'CalloutDialog' + CalloutDialog = 'CalloutDialog', + TableDesignerPublishDialog = 'TableDesignerPublishDialog' } export const enum TelemetryView { diff --git a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts index 5c8282c41d..b601fb5f4c 100644 --- a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts @@ -513,14 +513,20 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData const self = this; this._tableDesignerService.registerProvider(providerId, { providerId: providerId, - getTableDesignerInfo(tableInfo: azdata.designers.TableInfo): Thenable { - return self._proxy.$getTableDesignerInfo(handle, tableInfo); + initializeTableDesigner(tableInfo: azdata.designers.TableInfo): Thenable { + return self._proxy.$initializeTableDesigner(handle, tableInfo); }, - processTableEdit(table, data, edit): Thenable { - return self._proxy.$processTableDesignerEdit(handle, table, data, edit); + processTableEdit(table, edit): Thenable { + return self._proxy.$processTableDesignerEdit(handle, table, edit); }, - saveTable(tableInfo: azdata.designers.TableInfo, data: azdata.designers.DesignerViewModel): Thenable { - return self._proxy.$saveTable(handle, tableInfo, data); + publishChanges(tableInfo: azdata.designers.TableInfo): Thenable { + return self._proxy.$publishTableDesignerChanges(handle, tableInfo); + }, + generateScript(tableInfo: azdata.designers.TableInfo): Thenable { + return self._proxy.$generateScriptForTableDesigner(handle, tableInfo); + }, + generatePreviewReport(tableInfo: azdata.designers.TableInfo): Thenable { + return self._proxy.$generatePreviewReportForTableDesigner(handle, tableInfo); }, disposeTableDesigner(tableInfo: azdata.designers.TableInfo): Thenable { return self._proxy.$disposeTableDesigner(handle, tableInfo); diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index 69d8660659..3e1d96e144 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -892,16 +892,24 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { } // Table Designer - public override $getTableDesignerInfo(handle: number, table: azdata.designers.TableInfo): Thenable { - return this._resolveProvider(handle).getTableDesignerInfo(table); + public override $initializeTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { + return this._resolveProvider(handle).initializeTableDesigner(table); } - public override $processTableDesignerEdit(handle: number, table: azdata.designers.TableInfo, viewModel: azdata.designers.DesignerViewModel, edit: azdata.designers.DesignerEdit): Thenable { - return this._resolveProvider(handle).processTableEdit(table, viewModel, edit); + public override $processTableDesignerEdit(handle: number, table: azdata.designers.TableInfo, edit: azdata.designers.DesignerEdit): Thenable { + return this._resolveProvider(handle).processTableEdit(table, edit); } - public override $saveTable(handle: number, table: azdata.designers.TableInfo, viewModel: azdata.designers.DesignerViewModel): Thenable { - return this._resolveProvider(handle).saveTable(table, viewModel); + public override $publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable { + return this._resolveProvider(handle).publishChanges(table); + } + + public override $generateScriptForTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { + return this._resolveProvider(handle).generateScript(table); + } + + public override $generatePreviewReportForTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { + return this._resolveProvider(handle).generatePreviewReport(table); } public override $disposeTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 5dd7498fff..73b22209bd 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -530,17 +530,27 @@ export abstract class ExtHostDataProtocolShape { /** * Gets the table designer info for the specified table */ - $getTableDesignerInfo(handle: number, table: azdata.designers.TableInfo): Thenable { throw ni(); } + $initializeTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { throw ni(); } /** * Process the table edit. */ - $processTableDesignerEdit(handle: number, table: azdata.designers.TableInfo, data: azdata.designers.DesignerViewModel, edit: azdata.designers.DesignerEdit): Thenable { throw ni(); } + $processTableDesignerEdit(handle: number, table: azdata.designers.TableInfo, edit: azdata.designers.DesignerEdit): Thenable { throw ni(); } /** - * Process the table edit. + * Publish the table designer changes. */ - $saveTable(handle: number, table: azdata.designers.TableInfo, data: azdata.designers.DesignerViewModel): Thenable { throw ni(); } + $publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable { throw ni(); } + + /** + * Generate scripts. + */ + $generateScriptForTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { throw ni(); } + + /** + * Generate preview report. + */ + $generatePreviewReportForTableDesigner(handle: number, table: azdata.designers.TableInfo): Thenable { throw ni(); } /** * Dispose the table designer. diff --git a/src/sql/workbench/browser/designer/designer.ts b/src/sql/workbench/browser/designer/designer.ts index 90a25c8f33..a6b8dada17 100644 --- a/src/sql/workbench/browser/designer/designer.ts +++ b/src/sql/workbench/browser/designer/designer.ts @@ -331,7 +331,7 @@ export class Designer extends Disposable implements IThemable { let timeout; switch (action) { case 'save': - message = showLoading ? localize('designer.savingChanges', "Saving changes...") : localize('designer.savingChangesCompleted', "Changes have been saved"); + message = showLoading ? localize('designer.publishingChanges', "Publishing changes...") : localize('designer.publishChangesCompleted', "Changes have been published"); timeout = 0; break; case 'initialize': @@ -344,7 +344,9 @@ export class Designer extends Disposable implements IThemable { timeout = 500; break; default: - return; + message = showLoading ? localize('designer.processing', "Processing...") : localize('designer.processingCompleted', "Processing completed"); + timeout = 0; + break; } if (showLoading) { this.startLoading(message, useDelay ? timeout : 0); diff --git a/src/sql/workbench/browser/designer/interfaces.ts b/src/sql/workbench/browser/designer/interfaces.ts index 279d0555cd..bcb989c4b7 100644 --- a/src/sql/workbench/browser/designer/interfaces.ts +++ b/src/sql/workbench/browser/designer/interfaces.ts @@ -74,7 +74,7 @@ export interface DesignerUIState { activeTabId: PanelTabIdentifier; } -export type DesignerAction = 'save' | 'initialize' | 'processEdit'; +export type DesignerAction = 'save' | 'initialize' | 'processEdit' | 'generateScript' | 'generateReport'; export interface DesignerEditProcessedEventArgs { result: DesignerEditResult; diff --git a/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts b/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts index 41865d723c..4d424f34ac 100644 --- a/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts +++ b/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts @@ -81,7 +81,7 @@ export class TableDesignerInput extends EditorInput { } override async save(group: GroupIdentifier, options?: ISaveOptions): Promise { - await this._designerComponentInput.save(); + await this._designerComponentInput.openPublishDialog(); return this; } diff --git a/src/sql/workbench/contrib/tableDesigner/browser/actions.ts b/src/sql/workbench/contrib/tableDesigner/browser/actions.ts index 328896ccc2..24f316f61f 100644 --- a/src/sql/workbench/contrib/tableDesigner/browser/actions.ts +++ b/src/sql/workbench/contrib/tableDesigner/browser/actions.ts @@ -9,15 +9,12 @@ import { Codicon } from 'vs/base/common/codicons'; import { IDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -export class SaveTableChangesAction extends Action { - public static ID = 'tableDesigner.saveTableChanges'; - public static LABEL = localize('tableDesigner.saveTableChanges', "Save Changes"); - private _input: TableDesignerComponentInput; +export abstract class TableChangesActionBase extends Action { + protected _input: TableDesignerComponentInput; private _onStateChangeDisposable: IDisposable; - constructor( - ) { - super(SaveTableChangesAction.ID, SaveTableChangesAction.LABEL, Codicon.save.classNames); + constructor(id: string, label: string, iconClassNames: string) { + super(id, label, iconClassNames); } public setContext(input: TableDesignerComponentInput): void { @@ -29,10 +26,6 @@ export class SaveTableChangesAction extends Action { }); } - public override async run(): Promise { - await this._input.save(); - } - private updateState(): void { this.enabled = this._input.dirty && this._input.valid && this._input.pendingAction === undefined; } @@ -42,3 +35,28 @@ export class SaveTableChangesAction extends Action { this._onStateChangeDisposable?.dispose(); } } + +export class PublishTableChangesAction extends TableChangesActionBase { + public static ID = 'tableDesigner.publishTableChanges'; + public static LABEL = localize('tableDesigner.publishTableChanges', "Publish Changes..."); + constructor() { + super(PublishTableChangesAction.ID, PublishTableChangesAction.LABEL, Codicon.repoPush.classNames); + } + + public override async run(): Promise { + await this._input.openPublishDialog(); + } +} + +export class GenerateTableChangeScriptAction extends TableChangesActionBase { + public static ID = 'tableDesigner.generateScript'; + public static LABEL = localize('tableDesigner.generateScript', "Generate Script"); + + constructor() { + super(GenerateTableChangeScriptAction.ID, GenerateTableChangeScriptAction.LABEL, Codicon.output.classNames); + } + + public override async run(): Promise { + await this._input.generateScript(); + } +} diff --git a/src/sql/workbench/contrib/tableDesigner/browser/tableDesignerEditor.ts b/src/sql/workbench/contrib/tableDesigner/browser/tableDesignerEditor.ts index 36ebd1bc83..639cb29789 100644 --- a/src/sql/workbench/contrib/tableDesigner/browser/tableDesignerEditor.ts +++ b/src/sql/workbench/contrib/tableDesigner/browser/tableDesignerEditor.ts @@ -16,7 +16,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { SaveTableChangesAction } from 'sql/workbench/contrib/tableDesigner/browser/actions'; +import { GenerateTableChangeScriptAction, PublishTableChangesAction } from 'sql/workbench/contrib/tableDesigner/browser/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { DesignerPaneSeparator } from 'sql/platform/theme/common/colorRegistry'; @@ -25,7 +25,8 @@ export class TableDesignerEditor extends EditorPane { public static readonly ID: string = 'workbench.editor.tableDesigner'; private _designer: Designer; - private _saveChangesAction: SaveTableChangesAction; + private _publishChangesAction: PublishTableChangesAction; + private _generateScriptAction: GenerateTableChangeScriptAction; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -44,7 +45,8 @@ export class TableDesignerEditor extends EditorPane { await super.setInput(input, options, context, token); const designerInput = input.getComponentInput(); this._designer.setInput(designerInput); - this._saveChangesAction.setContext(designerInput); + this._publishChangesAction.setContext(designerInput); + this._generateScriptAction.setContext(designerInput); } protected createEditor(parent: HTMLElement): void { @@ -54,9 +56,11 @@ export class TableDesignerEditor extends EditorPane { const designerContainer = container.appendChild(DOM.$('.designer-container')); const actionbar = new ActionBar(actionbarContainer); this._register(actionbar); - this._saveChangesAction = this._instantiationService.createInstance(SaveTableChangesAction); - this._saveChangesAction.enabled = false; - actionbar.push(this._saveChangesAction, { icon: true, label: false }); + this._publishChangesAction = this._instantiationService.createInstance(PublishTableChangesAction); + this._publishChangesAction.enabled = false; + this._generateScriptAction = this._instantiationService.createInstance(GenerateTableChangeScriptAction); + this._generateScriptAction.enabled = false; + actionbar.push([this._publishChangesAction, this._generateScriptAction], { icon: true, label: false }); this._designer = this._instantiationService.createInstance(Designer, designerContainer); this._register(attachDesignerStyler(this._designer, this.themeService)); diff --git a/src/sql/workbench/services/tableDesigner/browser/media/tableDesignerPublishDialog.css b/src/sql/workbench/services/tableDesigner/browser/media/tableDesignerPublishDialog.css new file mode 100644 index 0000000000..5d02358cd2 --- /dev/null +++ b/src/sql/workbench/services/tableDesigner/browser/media/tableDesignerPublishDialog.css @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.table-designer-publish-dialog { + height: 350px; + padding: 10px; + overflow: scroll; + user-select: text; +} diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts index 6bb63ee33a..1a8a431065 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts @@ -11,6 +11,9 @@ import { designers } from 'sql/workbench/api/common/sqlExtHostTypes'; import { Emitter, Event } from 'vs/base/common/event'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { deepClone, equals } from 'vs/base/common/objects'; +import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TableDesignerPublishDialogResult, TableDesignerPublishDialog } from 'sql/workbench/services/tableDesigner/browser/tableDesignerPublishDialog'; export class TableDesignerComponentInput implements DesignerComponentInput { @@ -30,7 +33,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput { constructor(private readonly _provider: TableDesignerProvider, private _tableInfo: azdata.designers.TableInfo, - @INotificationService private readonly _notificationService: INotificationService) { + @INotificationService private readonly _notificationService: INotificationService, + @IQueryEditorService private readonly _queryEditorService: IQueryEditorService, + @IInstantiationService private readonly _instantiationService: IInstantiationService) { } get valid(): boolean { @@ -59,7 +64,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { processEdit(edit: DesignerEdit): void { this.updateState(this.valid, this.dirty, 'processEdit'); - this._provider.processTableEdit(this._tableInfo, this._viewModel!, edit).then( + this._provider.processTableEdit(this._tableInfo, edit).then( result => { this._viewModel = result.viewModel; this.updateState(result.isValid, !equals(this._viewModel, this._originalViewModel), undefined); @@ -79,24 +84,72 @@ export class TableDesignerComponentInput implements DesignerComponentInput { ); } - async save(): Promise { + async generateScript(): Promise { const notificationHandle = this._notificationService.notify({ severity: Severity.Info, - message: localize('tableDesigner.savingChanges', "Saving table designer changes...") + message: localize('tableDesigner.generatingScript', "Generating script..."), + sticky: true + }); + try { + this.updateState(this.valid, this.dirty, 'generateScript'); + const script = await this._provider.generateScript(this._tableInfo); + this._queryEditorService.newSqlEditor({ initalContent: script }); + this.updateState(this.valid, this.dirty); + notificationHandle.updateMessage(localize('tableDesigner.generatingScriptCompleted', "Script generated.")); + } catch (error) { + notificationHandle.updateSeverity(Severity.Error); + notificationHandle.updateMessage(localize('tableDesigner.generateScriptError', "An error occured while generating the script: {0}", error?.message ?? error)); + this.updateState(this.valid, this.dirty); + } + } + + async publishChanges(): Promise { + const saveNotificationHandle = this._notificationService.notify({ + severity: Severity.Info, + message: localize('tableDesigner.savingChanges', "Saving table designer changes..."), + sticky: true }); try { this.updateState(this.valid, this.dirty, 'save'); - await this._provider.saveTable(this._tableInfo, this._viewModel); + await this._provider.publishChanges(this._tableInfo); this._originalViewModel = this._viewModel; this.updateState(true, false); - notificationHandle.updateMessage(localize('tableDesigner.savedChangeSuccess', "The changes have been successfully saved.")); + saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeSuccess', "The changes have been successfully published.")); } catch (error) { - notificationHandle.updateSeverity(Severity.Error); - notificationHandle.updateMessage(localize('tableDesigner.saveChangeError', "An error occured while saving changes: {0}", error?.message ?? error)); + saveNotificationHandle.updateSeverity(Severity.Error); + saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeError', "An error occured while publishing changes: {0}", error?.message ?? error)); this.updateState(this.valid, this.dirty); } } + async openPublishDialog(): Promise { + const reportNotificationHandle = this._notificationService.notify({ + severity: Severity.Info, + message: localize('tableDesigner.generatingPreviewReport', "Generating preview report..."), + sticky: true + }); + + let report; + try { + this.updateState(this.valid, this.dirty, 'generateReport'); + report = await this._provider.generatePreviewReport(this._tableInfo); + reportNotificationHandle.close(); + this.updateState(this.valid, this.dirty); + } catch (error) { + reportNotificationHandle.updateSeverity(Severity.Error); + reportNotificationHandle.updateMessage(localize('tableDesigner.generatePreviewReportError', "An error occured while generating preview report: {0}", error?.message ?? error)); + this.updateState(this.valid, this.dirty); + return; + } + const dialog = this._instantiationService.createInstance(TableDesignerPublishDialog); + const result = await dialog.open(report); + if (result === TableDesignerPublishDialogResult.GenerateScript) { + await this.generateScript(); + } else if (result === TableDesignerPublishDialogResult.UpdateDatabase) { + await this.publishChanges(); + } + } + async revert(): Promise { this.updateState(true, false); } @@ -131,7 +184,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { } this.updateState(this.valid, this.dirty, 'initialize'); - this._provider.getTableDesignerInfo(this._tableInfo).then(result => { + this._provider.initializeTableDesigner(this._tableInfo).then(result => { this.doInitialization(result); this._onInitialized.fire(); }, error => { diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerPublishDialog.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerPublishDialog.ts new file mode 100644 index 0000000000..05b7efd265 --- /dev/null +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerPublishDialog.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/tableDesignerPublishDialog'; +import { Button } from 'sql/base/browser/ui/button/button'; +import { Modal } from 'sql/workbench/browser/modal/modal'; +import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { localize } from 'vs/nls'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import * as DOM from 'vs/base/browser/dom'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import { attachModalDialogStyler } from 'sql/workbench/common/styler'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; + +const OkText: string = localize('tableDesigner.UpdateDatabase', "Update Database"); +const CancelText: string = localize('tableDesigner.cancel', "Cancel"); +const GenerateScriptText: string = localize('tableDesigner.generateScript', "Generate Script"); + +export enum TableDesignerPublishDialogResult { + UpdateDatabase, + GenerateScript, + Cancel +} + +export class TableDesignerPublishDialog extends Modal { + + private _report?: string; + private _okButton?: Button; + private _generateScriptButton?: Button; + private _cancelButton?: Button; + private _promiseResolver: (value: TableDesignerPublishDialogResult) => void; + + constructor( + @IThemeService themeService: IThemeService, + @IClipboardService clipboardService: IClipboardService, + @ILayoutService layoutService: ILayoutService, + @IAdsTelemetryService telemetryService: IAdsTelemetryService, + @IContextKeyService contextKeyService: IContextKeyService, + @ILogService logService: ILogService, + @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService + ) { + super('', TelemetryKeys.ModalDialogName.TableDesignerPublishDialog, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'normal', hasTitleIcon: false }); + } + + public open(report: string): Promise { + this._report = report; + this.render(); + this.show(); + const promise = new Promise((resolve) => { + this._promiseResolver = resolve; + }); + return promise; + } + + public override render() { + super.render(); + this.title = localize('tableDesigner.previewDatabaseUpdates', "Preview Database Updates"); + this._register(attachModalDialogStyler(this, this._themeService)); + this._okButton = this.addFooterButton(OkText, () => this.handleOkButtonClick()); + this._generateScriptButton = this.addFooterButton(GenerateScriptText, () => this.handleGenerateScriptButtonClick(), 'right', true); + this._cancelButton = this.addFooterButton(CancelText, () => this.handleCancelButtonClick(), 'right', true); + this._register(attachButtonStyler(this._okButton, this._themeService)); + this._register(attachButtonStyler(this._generateScriptButton, this._themeService)); + this._register(attachButtonStyler(this._cancelButton, this._themeService)); + } + + protected renderBody(container: HTMLElement) { + const body = DOM.append(container, DOM.$('.table-designer-publish-dialog')); + body.innerText = this._report; + } + + protected layout(height?: number): void { + // Nothing to re-layout + } + + /* espace key */ + protected override onClose() { + this.handleCancelButtonClick(); + } + + /* enter key */ + protected override onAccept() { + this.handleOkButtonClick(); + } + + private handleOkButtonClick(): void { + this.hide('ok'); + this._promiseResolver(TableDesignerPublishDialogResult.UpdateDatabase); + } + + private handleGenerateScriptButtonClick(): void { + this.hide('ok'); + this._promiseResolver(TableDesignerPublishDialogResult.GenerateScript); + } + + private handleCancelButtonClick(): void { + this.hide('cancel'); + this._promiseResolver(TableDesignerPublishDialogResult.Cancel); + } +}