diff --git a/extensions/mssql/src/objectManagement/localizedConstants.ts b/extensions/mssql/src/objectManagement/localizedConstants.ts index 1dddcc3e1b..41f41f5c6c 100644 --- a/extensions/mssql/src/objectManagement/localizedConstants.ts +++ b/extensions/mssql/src/objectManagement/localizedConstants.ts @@ -53,7 +53,7 @@ export function RefreshObjectExplorerError(error: string): string { return localize({ key: 'objectManagement.refreshOEError', comment: ['{0}: error message.'] - }, "An error occurred while while refreshing the object explorer. {0}", error); + }, "An error occurred while refreshing the object explorer. {0}", error); } export function DeleteObjectConfirmationText(objectType: string, objectName: string): string { diff --git a/extensions/mssql/src/tableDesigner/tableDesigner.ts b/extensions/mssql/src/tableDesigner/tableDesigner.ts index 25934bcd24..7d639d1905 100644 --- a/extensions/mssql/src/tableDesigner/tableDesigner.ts +++ b/extensions/mssql/src/tableDesigner/tableDesigner.ts @@ -38,7 +38,7 @@ export function registerTableDesignerCommands(appContext: AppContext) { connectionString: connectionString, accessToken: context.connectionProfile!.options.azureAccountToken, tableIcon: tableIcon - }, telemetryInfo); + }, telemetryInfo, context); } catch (error) { console.error(error); await vscode.window.showErrorMessage(getErrorMessage(error), { modal: true }); @@ -70,7 +70,7 @@ export function registerTableDesignerCommands(appContext: AppContext) { connectionString: connectionString, accessToken: context.connectionProfile!.options.azureAccountToken, tableIcon: tableIcon - }, telemetryInfo); + }, telemetryInfo, context); } catch (error) { console.error(error); await vscode.window.showErrorMessage(getErrorMessage(error), { modal: true }); diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index da30b5d850..39351fbd1a 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -893,8 +893,9 @@ declare module 'azdata' { * @param providerId The table designer provider Id. * @param tableInfo The table information. The object will be passed back to the table designer provider as the unique identifier for the table. * @param telemetryInfo Optional Key-value pair containing any extra information that needs to be sent via telemetry + * @param objectExplorerContext Optional The context used to refresh Object Explorer after the table is created or edited */ - export function openTableDesigner(providerId: string, tableInfo: TableInfo, telemetryInfo?: { [key: string]: string }): Thenable; + export function openTableDesigner(providerId: string, tableInfo: TableInfo, telemetryInfo?: { [key: string]: string }, objectExplorerContext?: ObjectExplorerContext): Thenable; /** * Definition for the table designer provider. diff --git a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts index ceb75a55ac..c202bb657c 100644 --- a/src/sql/workbench/api/browser/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts @@ -668,8 +668,8 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData } // Table Designer - public $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): void { - this._tableDesignerService.openTableDesigner(providerId, tableInfo, telemetryInfo); + public $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): void { + this._tableDesignerService.openTableDesigner(providerId, tableInfo, telemetryInfo, objectExplorerContext); } public $unregisterProvider(handle: number): Promise { diff --git a/src/sql/workbench/api/common/extHostDataProtocol.ts b/src/sql/workbench/api/common/extHostDataProtocol.ts index 6529e04db4..ca86392cf5 100644 --- a/src/sql/workbench/api/common/extHostDataProtocol.ts +++ b/src/sql/workbench/api/common/extHostDataProtocol.ts @@ -946,8 +946,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { return this._resolveProvider(handle).disposeTableDesigner(table); } - public override $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): Promise { - this._proxy.$openTableDesigner(providerId, tableInfo, telemetryInfo); + public override $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): Promise { + this._proxy.$openTableDesigner(providerId, tableInfo, telemetryInfo, objectExplorerContext); return Promise.resolve(); } diff --git a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts index a1a9b8fa26..6aa1f47dc2 100644 --- a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts @@ -608,8 +608,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp TableIndexColumnSpecificationProperty: sqlExtHostTypes.designers.TableIndexColumnSpecificationProperty, DesignerEditType: sqlExtHostTypes.designers.DesignerEditType, TableIcon: sqlExtHostTypes.designers.TableIcon, - openTableDesigner(providerId, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): Promise { - return extHostDataProvider.$openTableDesigner(providerId, tableInfo, telemetryInfo); + openTableDesigner(providerId, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): Promise { + return extHostDataProvider.$openTableDesigner(providerId, tableInfo, telemetryInfo, objectExplorerContext); } }; diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 4d6b4185d8..97f533a4de 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -581,7 +581,7 @@ export abstract class ExtHostDataProtocolShape { /** * Open a new instance of table designer. */ - $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): void { throw ni(); } + $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): void { throw ni(); } /** * Gets the generic execution plan graph for a plan file. @@ -710,7 +710,7 @@ export interface MainThreadDataProtocolShape extends IDisposable { $onSessionStopped(handle: number, response: azdata.ProfilerSessionStoppedParams): void; $onProfilerSessionCreated(handle: number, response: azdata.ProfilerSessionCreatedParams): void; $onJobDataUpdated(handle: Number): void; - $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): void; + $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): void; /** * Callback when a session has completed initialization */ diff --git a/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts b/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts index 070413b392..42d7a1348e 100644 --- a/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts +++ b/src/sql/workbench/browser/editor/tableDesigner/tableDesignerInput.ts @@ -39,10 +39,11 @@ export class TableDesignerInput extends EditorInput { private _provider: TableDesignerProvider, tableInfo: azdata.designers.TableInfo, telemetryInfo: { [key: string]: string }, + objectExplorerContext: azdata.ObjectExplorerContext, @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotificationService private readonly _notificationService: INotificationService) { super(); - this._designerComponentInput = this._instantiationService.createInstance(TableDesignerComponentInput, this._provider, tableInfo, telemetryInfo); + this._designerComponentInput = this._instantiationService.createInstance(TableDesignerComponentInput, this._provider, tableInfo, telemetryInfo, objectExplorerContext); this._register(this._designerComponentInput.onStateChange((e) => { if (e.previousState.pendingAction === 'publish') { this.setEditorLabel(); diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts index 5ef2a5e7a8..f336504a37 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts @@ -19,6 +19,7 @@ import { TelemetryAction, TelemetryView } from 'sql/platform/telemetry/common/te import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; import { TableDesignerMetadata } from 'sql/workbench/services/tableDesigner/browser/tableDesignerMetadata'; import { Queue, timeout } from 'vs/base/common/async'; +import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; const ErrorDialogTitle: string = localize('tableDesigner.ErrorDialogTitle', "Table Designer Error"); export class TableDesignerComponentInput implements DesignerComponentInput { @@ -55,11 +56,13 @@ export class TableDesignerComponentInput implements DesignerComponentInput { constructor(private readonly _provider: TableDesignerProvider, public tableInfo: azdata.designers.TableInfo, private _telemetryInfo: ITelemetryEventProperties, + private _objectExplorerContext: azdata.ObjectExplorerContext, @INotificationService private readonly _notificationService: INotificationService, @IAdsTelemetryService readonly _adsTelemetryService: IAdsTelemetryService, @IQueryEditorService private readonly _queryEditorService: IQueryEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IErrorMessageService private readonly _errorMessageService: IErrorMessageService) { + @IErrorMessageService private readonly _errorMessageService: IErrorMessageService, + @IObjectExplorerService private readonly _objectExplorerService: IObjectExplorerService) { } public designerUIState?: DesignerUIState = undefined; @@ -138,6 +141,8 @@ export class TableDesignerComponentInput implements DesignerComponentInput { sticky: true }); const startTime = new Date().getTime(); + let isPublishSuccessful = false; + const isNewTable = this.tableInfo.isNewTable; try { this.updateState(this.valid, this.dirty, 'publish'); const result = await this._provider.publishChanges(this.tableInfo); @@ -152,11 +157,48 @@ export class TableDesignerComponentInput implements DesignerComponentInput { publishEvent.withAdditionalMeasurements({ 'elapsedTimeMs': new Date().getTime() - startTime }).withAdditionalProperties(metadataTelemetryInfo).send(); + isPublishSuccessful = true; } catch (error) { this._errorMessageService.showDialog(Severity.Error, ErrorDialogTitle, localize('tableDesigner.publishChangeError', "An error occured while publishing changes: {0}", error?.message ?? error), error?.data); this.updateState(this.valid, this.dirty); this._adsTelemetryService.createErrorEvent(TelemetryView.TableDesigner, TelemetryAction.PublishChanges).withAdditionalProperties(telemetryInfo).send(); } + + if (isPublishSuccessful) { + await this.refreshNodeInOE(isNewTable); + } + } + + private async refreshNodeInOE(isNewTable: boolean) { + if (!this._objectExplorerContext) { + return; + } + + try { + const connectionId = this._objectExplorerContext.connectionProfile?.id; + const nodeInfo = this._objectExplorerContext.nodeInfo; + const node = await this._objectExplorerService.getTreeNode(connectionId, nodeInfo?.nodePath); + let refreshNodePath: string; + + if (isNewTable) { + refreshNodePath = nodeInfo?.nodePath; + } else { + // This handle the case where a user publishes a table then edit the same table within the same designer then publish again. In that case node path in OE context is already correct. + if (node?.objectType === 'Tables') { + refreshNodePath = nodeInfo?.nodePath + } else { + refreshNodePath = nodeInfo?.parentNodePath; + } + } + + await this._objectExplorerService.refreshNodeInView(connectionId, refreshNodePath); + } catch (error) { + const errorMessage = localize({ + key: 'tableDesigner.refreshOEError', + comment: ['{0}: error message.'] + }, "An error occurred while refreshing the object explorer. {0}", error); + this._notificationService.error(errorMessage); + } } async save(): Promise { diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts index 74a13a74df..38b0f0584e 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerService.ts @@ -65,10 +65,10 @@ export class TableDesignerService implements ITableDesignerService { throw invalidProvider(providerId); } - public async openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): Promise { + public async openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): Promise { this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.Open).withAdditionalProperties(telemetryInfo).send(); const provider = this.getProvider(providerId); - const tableDesignerInput = this._instantiationService.createInstance(TableDesignerInput, provider, tableInfo, telemetryInfo); + const tableDesignerInput = this._instantiationService.createInstance(TableDesignerInput, provider, tableInfo, telemetryInfo, objectExplorerContext); await this._editorService.openEditor(tableDesignerInput, { pinned: true }, ACTIVE_GROUP); } } diff --git a/src/sql/workbench/services/tableDesigner/common/interface.ts b/src/sql/workbench/services/tableDesigner/common/interface.ts index 2918f7fec5..b7cb55c648 100644 --- a/src/sql/workbench/services/tableDesigner/common/interface.ts +++ b/src/sql/workbench/services/tableDesigner/common/interface.ts @@ -36,6 +36,8 @@ export interface ITableDesignerService { * Open a table designer for the given table * @param providerId The provider id * @param tableInfo The table information + * @param telemetryInfo Telemetry information + * @param objectExplorerContext The object explorer context */ - openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties): Promise; + openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, telemetryInfo?: ITelemetryEventProperties, objectExplorerContext?: azdata.ObjectExplorerContext): Promise; }