From 005fc9e4df55b89752f694672c36054e9f5bf54c Mon Sep 17 00:00:00 2001 From: Karl Burtram Date: Thu, 13 Jul 2017 07:18:34 -0700 Subject: [PATCH] Add try/catch blocks for request handlers (#408) * Add try/catch for request handlers * Fix test break --- .../DisasterRecoveryService.cs | 195 ++++++---- .../Formatter/TSqlFormatterService.cs | 2 +- .../LanguageServices/LanguageService.cs | 363 +++++++++++------- .../ObjectExplorer/ObjectExplorerService.cs | 75 ++-- .../Workspace/WorkspaceService.cs | 107 ++++-- .../Workspace/WorkspaceTests.cs | 5 +- 6 files changed, 441 insertions(+), 306 deletions(-) diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs index 02f2cc8a..0f25faa4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs @@ -88,25 +88,32 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery DefaultDatabaseInfoParams optionsParams, RequestContext requestContext) { - var response = new BackupConfigInfoResponse(); - ConnectionInfo connInfo; - DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection( - optionsParams.OwnerUri, - out connInfo); - - if (connInfo != null) + try { - DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); - SqlConnection sqlConn = GetSqlConnection(connInfo); - if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) - { - BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database); - backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo); - response.BackupConfigInfo = backupConfigInfo; - } - } + var response = new BackupConfigInfoResponse(); + ConnectionInfo connInfo; + DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection( + optionsParams.OwnerUri, + out connInfo); - await requestContext.SendResult(response); + if (connInfo != null) + { + DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); + SqlConnection sqlConn = GetSqlConnection(connInfo); + if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) + { + BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database); + backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo); + response.BackupConfigInfo = backupConfigInfo; + } + } + + await requestContext.SendResult(response); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } } /// @@ -116,22 +123,28 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery RestoreParams restoreParams, RequestContext requestContext) { - RestorePlanResponse response = new RestorePlanResponse(); - ConnectionInfo connInfo; - bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo); - - if (supported && connInfo != null) + try { - RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams); - response = this.restoreDatabaseService.CreateRestorePlanResponse(restoreDataObject); - } - else - { - response.CanRestore = false; - response.ErrorMessage = SR.RestoreNotSupported; - } - await requestContext.SendResult(response); + RestorePlanResponse response = new RestorePlanResponse(); + ConnectionInfo connInfo; + bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo); + if (supported && connInfo != null) + { + RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams); + response = this.restoreDatabaseService.CreateRestorePlanResponse(restoreDataObject); + } + else + { + response.CanRestore = false; + response.ErrorMessage = SR.RestoreNotSupported; + } + await requestContext.SendResult(response); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } } /// @@ -141,46 +154,53 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery RestoreParams restoreParams, RequestContext requestContext) { - RestoreResponse response = new RestoreResponse(); - ConnectionInfo connInfo; - bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo); - - if (supported && connInfo != null) + try { - try + RestoreResponse response = new RestoreResponse(); + ConnectionInfo connInfo; + bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo); + + if (supported && connInfo != null) { - RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams); - - if (restoreDataObject != null) + try { - // create task metadata - TaskMetadata metadata = new TaskMetadata(); - metadata.ServerName = connInfo.ConnectionDetails.ServerName; - metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; - metadata.Name = SR.RestoreTaskName; - metadata.IsCancelable = true; - metadata.Data = restoreDataObject; + RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams); - // create restore task and perform - SqlTask sqlTask = SqlTaskManager.Instance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync); - response.TaskId = sqlTask.TaskId.ToString(); + if (restoreDataObject != null) + { + // create task metadata + TaskMetadata metadata = new TaskMetadata(); + metadata.ServerName = connInfo.ConnectionDetails.ServerName; + metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; + metadata.Name = SR.RestoreTaskName; + metadata.IsCancelable = true; + metadata.Data = restoreDataObject; + + // create restore task and perform + SqlTask sqlTask = SqlTaskManager.Instance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync); + response.TaskId = sqlTask.TaskId.ToString(); + } + else + { + response.ErrorMessage = SR.RestorePlanFailed; + } } - else + catch (Exception ex) { - response.ErrorMessage = SR.RestorePlanFailed; + response.ErrorMessage = ex.Message; } } - catch (Exception ex) + else { - response.ErrorMessage = ex.Message; + response.ErrorMessage = SR.RestoreNotSupported; } - } - else - { - response.ErrorMessage = SR.RestoreNotSupported; - } - await requestContext.SendResult(response); + await requestContext.SendResult(response); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } } /// @@ -189,35 +209,42 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery internal static async Task HandleBackupRequest( BackupParams backupParams, RequestContext requestContext) - { - ConnectionInfo connInfo; - DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection( - backupParams.OwnerUri, - out connInfo); + { + try + { + ConnectionInfo connInfo; + DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection( + backupParams.OwnerUri, + out connInfo); - if (connInfo != null) - { - DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); - SqlConnection sqlConn = GetSqlConnection(connInfo); - if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) + if (connInfo != null) { - BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo); + DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); + SqlConnection sqlConn = GetSqlConnection(connInfo); + if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) + { + BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo); - // create task metadata - TaskMetadata metadata = new TaskMetadata(); - metadata.ServerName = connInfo.ConnectionDetails.ServerName; - metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; - metadata.Name = SR.Backup_TaskName; - metadata.IsCancelable = true; - metadata.Data = backupOperation; - - // create backup task and perform - SqlTask sqlTask = SqlTaskManager.Instance.CreateTask(metadata, Instance.BackupTaskAsync); - sqlTask.Run(); + // create task metadata + TaskMetadata metadata = new TaskMetadata(); + metadata.ServerName = connInfo.ConnectionDetails.ServerName; + metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; + metadata.Name = SR.Backup_TaskName; + metadata.IsCancelable = true; + metadata.Data = backupOperation; + + // create backup task and perform + SqlTask sqlTask = SqlTaskManager.Instance.CreateTask(metadata, Instance.BackupTaskAsync); + sqlTask.Run(); + } } + + await requestContext.SendResult(new BackupResponse()); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); } - - await requestContext.SendResult(new BackupResponse()); } internal static SqlConnection GetSqlConnection(ConnectionInfo connInfo) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Formatter/TSqlFormatterService.cs b/src/Microsoft.SqlTools.ServiceLayer/Formatter/TSqlFormatterService.cs index 8b8ab2a9..f13b9e2f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Formatter/TSqlFormatterService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Formatter/TSqlFormatterService.cs @@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Formatter EventContext eventContext) { // update the current settings to reflect any changes (assuming formatter settings exist) - settings = newSettings.SqlTools.Format ?? settings; + settings = newSettings?.SqlTools?.Format ?? settings; return Task.FromResult(true); } diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs index ebfa8da4..87001466 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs @@ -287,34 +287,41 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices internal async Task HandleCompletionRequest( TextDocumentPosition textDocumentPosition, RequestContext requestContext) - { - // check if Intellisense suggestions are enabled - if (ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri)) - { - await requestContext.SendResult(null); - } - else - { - // get the current list of completion items and return to client - var scriptFile = CurrentWorkspace.GetFile( - textDocumentPosition.TextDocument.Uri); - if (scriptFile == null) + { + try + { + // check if Intellisense suggestions are enabled + if (ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri)) { await requestContext.SendResult(null); - return; } + else + { + // get the current list of completion items and return to client + var scriptFile = CurrentWorkspace.GetFile( + textDocumentPosition.TextDocument.Uri); + if (scriptFile == null) + { + await requestContext.SendResult(null); + return; + } - ConnectionInfo connInfo; - ConnectionServiceInstance.TryFindConnection( - scriptFile.ClientFilePath, - out connInfo); + ConnectionInfo connInfo; + ConnectionServiceInstance.TryFindConnection( + scriptFile.ClientFilePath, + out connInfo); - var completionItems = GetCompletionItems( - textDocumentPosition, scriptFile, connInfo); + var completionItems = GetCompletionItems( + textDocumentPosition, scriptFile, connInfo); - await requestContext.SendResult(completionItems); + await requestContext.SendResult(completionItems); } } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } + } /// /// Handle the resolve completion request event to provide additional @@ -327,59 +334,73 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices CompletionItem completionItem, RequestContext requestContext) { - // check if Intellisense suggestions are enabled - // Note: Do not know file, so no need to check for MSSQL flavor - if (!CurrentWorkspaceSettings.IsSuggestionsEnabled) + try { - await requestContext.SendResult(completionItem); + // check if Intellisense suggestions are enabled + // Note: Do not know file, so no need to check for MSSQL flavor + if (!CurrentWorkspaceSettings.IsSuggestionsEnabled) + { + await requestContext.SendResult(completionItem); + } + else + { + completionItem = ResolveCompletionItem(completionItem); + await requestContext.SendResult(completionItem); + } } - else + catch (Exception ex) { - completionItem = ResolveCompletionItem(completionItem); - await requestContext.SendResult(completionItem); + await requestContext.SendError(ex.ToString()); } } internal async Task HandleDefinitionRequest(TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - DocumentStatusHelper.SendStatusChange(requestContext, textDocumentPosition, DocumentStatusHelper.DefinitionRequested); - - if (!ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri)) + try { - // Retrieve document and connection - ConnectionInfo connInfo; - var scriptFile = CurrentWorkspace.GetFile(textDocumentPosition.TextDocument.Uri); - bool isConnected = false; - bool succeeded = false; - DefinitionResult definitionResult = null; - if (scriptFile != null) + DocumentStatusHelper.SendStatusChange(requestContext, textDocumentPosition, DocumentStatusHelper.DefinitionRequested); + + if (!ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri)) { - isConnected = ConnectionServiceInstance.TryFindConnection(scriptFile.ClientFilePath, out connInfo); - definitionResult = GetDefinition(textDocumentPosition, scriptFile, connInfo); - } - - if (definitionResult != null) - { - if (definitionResult.IsErrorResult) + // Retrieve document and connection + ConnectionInfo connInfo; + var scriptFile = CurrentWorkspace.GetFile(textDocumentPosition.TextDocument.Uri); + bool isConnected = false; + bool succeeded = false; + DefinitionResult definitionResult = null; + if (scriptFile != null) { - await requestContext.SendError(definitionResult.Message); + isConnected = ConnectionServiceInstance.TryFindConnection(scriptFile.ClientFilePath, out connInfo); + definitionResult = GetDefinition(textDocumentPosition, scriptFile, connInfo); + } + + if (definitionResult != null) + { + if (definitionResult.IsErrorResult) + { + await requestContext.SendError(definitionResult.Message); + } + else + { + await requestContext.SendResult(definitionResult.Locations); + succeeded = true; + } } else { - await requestContext.SendResult(definitionResult.Locations); - succeeded = true; + // Send an empty result so that processing does not hang + await requestContext.SendResult(Array.Empty()); } - } - else - { - // Send an empty result so that processing does not hang - await requestContext.SendResult(Array.Empty()); + + DocumentStatusHelper.SendTelemetryEvent(requestContext, CreatePeekTelemetryProps(succeeded, isConnected)); } - DocumentStatusHelper.SendTelemetryEvent(requestContext, CreatePeekTelemetryProps(succeeded, isConnected)); + DocumentStatusHelper.SendStatusChange(requestContext, textDocumentPosition, DocumentStatusHelper.DefinitionRequestCompleted); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); } - - DocumentStatusHelper.SendStatusChange(requestContext, textDocumentPosition, DocumentStatusHelper.DefinitionRequestCompleted); } private static TelemetryProperties CreatePeekTelemetryProps(bool succeeded, bool connected) @@ -416,54 +437,68 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - // check if Intellisense suggestions are enabled - if (ShouldSkipNonMssqlFile(textDocumentPosition)) + try { - await requestContext.SendResult(null); - } - else - { - ScriptFile scriptFile = CurrentWorkspace.GetFile( - textDocumentPosition.TextDocument.Uri); - SignatureHelp help = null; - if (scriptFile != null) + // check if Intellisense suggestions are enabled + if (ShouldSkipNonMssqlFile(textDocumentPosition)) { - help = GetSignatureHelp(textDocumentPosition, scriptFile); - } - if (help != null) - { - await requestContext.SendResult(help); + await requestContext.SendResult(null); } else { - await requestContext.SendResult(new SignatureHelp()); + ScriptFile scriptFile = CurrentWorkspace.GetFile( + textDocumentPosition.TextDocument.Uri); + SignatureHelp help = null; + if (scriptFile != null) + { + help = GetSignatureHelp(textDocumentPosition, scriptFile); + } + if (help != null) + { + await requestContext.SendResult(help); + } + else + { + await requestContext.SendResult(new SignatureHelp()); + } } } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } } private async Task HandleHoverRequest( TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - // check if Quick Info hover tooltips are enabled - if (CurrentWorkspaceSettings.IsQuickInfoEnabled - && !ShouldSkipNonMssqlFile(textDocumentPosition)) + try { - var scriptFile = CurrentWorkspace.GetFile( - textDocumentPosition.TextDocument.Uri); + // check if Quick Info hover tooltips are enabled + if (CurrentWorkspaceSettings.IsQuickInfoEnabled + && !ShouldSkipNonMssqlFile(textDocumentPosition)) + { + var scriptFile = CurrentWorkspace.GetFile( + textDocumentPosition.TextDocument.Uri); - Hover hover = null; - if (scriptFile != null) - { - hover = GetHoverItem(textDocumentPosition, scriptFile); - } - if (hover != null) - { - await requestContext.SendResult(hover); + Hover hover = null; + if (scriptFile != null) + { + hover = GetHoverItem(textDocumentPosition, scriptFile); + } + if (hover != null) + { + await requestContext.SendResult(hover); + } } + + await requestContext.SendResult(new Hover()); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); } - - await requestContext.SendResult(new Hover()); } #endregion @@ -480,16 +515,24 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices ScriptFile scriptFile, EventContext eventContext) { - // if not in the preview window and diagnostics are enabled then run diagnostics - if (!IsPreviewWindow(scriptFile) - && CurrentWorkspaceSettings.IsDiagnosticsEnabled) + try { - await RunScriptDiagnostics( - new ScriptFile[] { scriptFile }, - eventContext); - } + // if not in the preview window and diagnostics are enabled then run diagnostics + if (!IsPreviewWindow(scriptFile) + && CurrentWorkspaceSettings.IsDiagnosticsEnabled) + { + await RunScriptDiagnostics( + new ScriptFile[] { scriptFile }, + eventContext); + } - await Task.FromResult(true); + await Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // TODO: need mechanism return errors from event handlers + } } /// @@ -499,15 +542,23 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices /// public async Task HandleDidChangeTextDocumentNotification(ScriptFile[] changedFiles, EventContext eventContext) { - if (CurrentWorkspaceSettings.IsDiagnosticsEnabled) + try { - // Only process files that are MSSQL flavor - await this.RunScriptDiagnostics( - changedFiles.ToArray(), - eventContext); - } + if (CurrentWorkspaceSettings.IsDiagnosticsEnabled) + { + // Only process files that are MSSQL flavor + await this.RunScriptDiagnostics( + changedFiles.ToArray(), + eventContext); + } - await Task.FromResult(true); + await Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // TODO: need mechanism return errors from event handlers + } } /// @@ -594,32 +645,78 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices SqlToolsSettings oldSettings, EventContext eventContext) { - bool oldEnableIntelliSense = oldSettings.SqlTools.IntelliSense.EnableIntellisense; - bool? oldEnableDiagnostics = oldSettings.SqlTools.IntelliSense.EnableErrorChecking; - - // update the current settings to reflect any changes - CurrentWorkspaceSettings.Update(newSettings); - - // if script analysis settings have changed we need to clear the current diagnostic markers - if (oldEnableIntelliSense != newSettings.SqlTools.IntelliSense.EnableIntellisense - || oldEnableDiagnostics != newSettings.SqlTools.IntelliSense.EnableErrorChecking) + try { - // if the user just turned off diagnostics then send an event to clear the error markers - if (!newSettings.IsDiagnosticsEnabled) - { - ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0]; + bool oldEnableIntelliSense = oldSettings.SqlTools.IntelliSense.EnableIntellisense; + bool? oldEnableDiagnostics = oldSettings.SqlTools.IntelliSense.EnableErrorChecking; - foreach (var scriptFile in CurrentWorkspace.GetOpenedFiles()) + // update the current settings to reflect any changes + CurrentWorkspaceSettings.Update(newSettings); + + // if script analysis settings have changed we need to clear the current diagnostic markers + if (oldEnableIntelliSense != newSettings.SqlTools.IntelliSense.EnableIntellisense + || oldEnableDiagnostics != newSettings.SqlTools.IntelliSense.EnableErrorChecking) + { + // if the user just turned off diagnostics then send an event to clear the error markers + if (!newSettings.IsDiagnosticsEnabled) { - await DiagnosticsHelper.ClearScriptDiagnostics(scriptFile.ClientFilePath, eventContext); + ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0]; + + foreach (var scriptFile in CurrentWorkspace.GetOpenedFiles()) + { + await DiagnosticsHelper.ClearScriptDiagnostics(scriptFile.ClientFilePath, eventContext); + } + } + // otherwise rerun diagnostic analysis on all opened SQL files + else + { + await this.RunScriptDiagnostics(CurrentWorkspace.GetOpenedFiles(), eventContext); + } + } + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // TODO: need mechanism return errors from event handlers + } + } + + /// + /// Handles language flavor changes by disabling intellisense on a file if it does not match the specific + /// "MSSQL" language flavor returned by our service + /// + /// + public async Task HandleDidChangeLanguageFlavorNotification( + LanguageFlavorChangeParams changeParams, + EventContext eventContext) + { + try + { + Validate.IsNotNull(nameof(changeParams), changeParams); + Validate.IsNotNull(nameof(changeParams), changeParams.Uri); + bool shouldBlock = false; + if (SQL_LANG.Equals(changeParams.Language, StringComparison.OrdinalIgnoreCase)) { + shouldBlock = !ServiceHost.ProviderName.Equals(changeParams.Flavor, StringComparison.OrdinalIgnoreCase); + } + + if (shouldBlock) { + this.nonMssqlUriMap.AddOrUpdate(changeParams.Uri, true, (k, oldValue) => true); + if (CurrentWorkspace.ContainsFile(changeParams.Uri)) + { + await DiagnosticsHelper.ClearScriptDiagnostics(changeParams.Uri, eventContext); } } - // otherwise rerun diagnostic analysis on all opened SQL files else { - await this.RunScriptDiagnostics(CurrentWorkspace.GetOpenedFiles(), eventContext); + bool value; + this.nonMssqlUriMap.TryRemove(changeParams.Uri, out value); } } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // TODO: need mechanism return errors from event handlers + } } #endregion @@ -852,36 +949,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices } } - /// - /// Handles language flavor changes by disabling intellisense on a file if it does not match the specific - /// "MSSQL" language flavor returned by our service - /// - /// - public async Task HandleDidChangeLanguageFlavorNotification( - LanguageFlavorChangeParams changeParams, - EventContext eventContext) - { - Validate.IsNotNull(nameof(changeParams), changeParams); - Validate.IsNotNull(nameof(changeParams), changeParams.Uri); - bool shouldBlock = false; - if (SQL_LANG.Equals(changeParams.Language, StringComparison.OrdinalIgnoreCase)) { - shouldBlock = !ServiceHost.ProviderName.Equals(changeParams.Flavor, StringComparison.OrdinalIgnoreCase); - } - - if (shouldBlock) { - this.nonMssqlUriMap.AddOrUpdate(changeParams.Uri, true, (k, oldValue) => true); - if (CurrentWorkspace.ContainsFile(changeParams.Uri)) - { - await DiagnosticsHelper.ClearScriptDiagnostics(changeParams.Uri, eventContext); - } - } - else - { - bool value; - this.nonMssqlUriMap.TryRemove(changeParams.Uri, out value); - } - } - private bool ShouldSkipNonMssqlFile(TextDocumentPosition textDocPosition) { return ShouldSkipNonMssqlFile(textDocPosition.TextDocument.Uri); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs index a83f13b6..0a486bae 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs @@ -134,31 +134,37 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer EventContext eventContext) { // update the current settings to reflect any changes (assuming formatter settings exist) - settings = newSettings.SqlTools.ObjectExplorer ?? settings; + settings = newSettings?.SqlTools?.ObjectExplorer ?? settings; return Task.FromResult(true); } internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext context) { - Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest"); - - Func> doCreateSession = async () => + try { - Validate.IsNotNull(nameof(connectionDetails), connectionDetails); - Validate.IsNotNull(nameof(context), context); - return await Task.Factory.StartNew(() => + Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest"); + Func> doCreateSession = async () => { - string uri = GenerateUri(connectionDetails); + Validate.IsNotNull(nameof(connectionDetails), connectionDetails); + Validate.IsNotNull(nameof(context), context); + return await Task.Factory.StartNew(() => + { + string uri = GenerateUri(connectionDetails); - return new CreateSessionResponse { SessionId = uri }; - }); - }; + return new CreateSessionResponse { SessionId = uri }; + }); + }; - CreateSessionResponse response = await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest"); - if (response != null) + CreateSessionResponse response = await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest"); + if (response != null) + { + RunCreateSessionTask(connectionDetails, response.SessionId); + } + } + catch (Exception ex) { - RunCreateSessionTask(connectionDetails, response.SessionId); + await context.SendError(ex.ToString()); } } @@ -191,32 +197,39 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer return true; } }; - await HandleRequestAsync(expandNode, context, "HandleExpandRequest"); + await HandleRequestAsync(expandNode, context, "HandleExpandRequest"); } internal async Task HandleRefreshRequest(RefreshParams refreshParams, RequestContext context) { - Logger.Write(LogLevel.Verbose, "HandleRefreshRequest"); - Validate.IsNotNull(nameof(refreshParams), refreshParams); - Validate.IsNotNull(nameof(context), context); + try + { + Logger.Write(LogLevel.Verbose, "HandleRefreshRequest"); + Validate.IsNotNull(nameof(refreshParams), refreshParams); + Validate.IsNotNull(nameof(context), context); - string uri = refreshParams.SessionId; - ObjectExplorerSession session = null; - if (!sessionMap.TryGetValue(uri, out session)) - { - Logger.Write(LogLevel.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} "); - await serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse + string uri = refreshParams.SessionId; + ObjectExplorerSession session = null; + if (!sessionMap.TryGetValue(uri, out session)) { - SessionId = refreshParams.SessionId, - NodePath = refreshParams.NodePath, - ErrorMessage = $"Couldn't find session for session: {uri}" - }); + Logger.Write(LogLevel.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} "); + await serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse + { + SessionId = refreshParams.SessionId, + NodePath = refreshParams.NodePath, + ErrorMessage = $"Couldn't find session for session: {uri}" + }); + } + else + { + RunExpandTask(session, refreshParams, true); + } + await context.SendResult(true); } - else + catch (Exception ex) { - RunExpandTask(session, refreshParams, true); + await context.SendError(ex.ToString()); } - await context.SendResult(true); } internal async Task HandleCloseSessionRequest(CloseSessionParams closeSessionParams, RequestContext context) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs b/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs index 485adfc0..4df336d6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs @@ -232,8 +232,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace var handlers = TextDocChangeCallbacks.Select(t => t(changedFiles.ToArray(), eventContext)); return Task.WhenAll(handlers); } - catch + catch (Exception ex) { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); // Swallow exceptions here to prevent us from crashing // TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here return Task.FromResult(true); @@ -244,50 +245,70 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace DidOpenTextDocumentNotification openParams, EventContext eventContext) { - Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification"); - - if (IsScmEvent(openParams.TextDocument.Uri)) + try { + Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification"); + + if (IsScmEvent(openParams.TextDocument.Uri)) + { + return; + } + + // read the SQL file contents into the ScriptFile + ScriptFile openedFile = Workspace.GetFileBuffer(openParams.TextDocument.Uri, openParams.TextDocument.Text); + if (openedFile == null) + { + return; + } + // Propagate the changes to the event handlers + var textDocOpenTasks = TextDocOpenCallbacks.Select( + t => t(openedFile, eventContext)); + + await Task.WhenAll(textDocOpenTasks); + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // Swallow exceptions here to prevent us from crashing + // TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here return; } - - // read the SQL file contents into the ScriptFile - ScriptFile openedFile = Workspace.GetFileBuffer(openParams.TextDocument.Uri, openParams.TextDocument.Text); - if (openedFile == null) - { - return; - } - // Propagate the changes to the event handlers - var textDocOpenTasks = TextDocOpenCallbacks.Select( - t => t(openedFile, eventContext)); - - await Task.WhenAll(textDocOpenTasks); } internal async Task HandleDidCloseTextDocumentNotification( DidCloseTextDocumentParams closeParams, EventContext eventContext) { - Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification"); - - if (IsScmEvent(closeParams.TextDocument.Uri)) + try { + Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification"); + + if (IsScmEvent(closeParams.TextDocument.Uri)) + { + return; + } + + // Skip closing this file if the file doesn't exist + var closedFile = Workspace.GetFile(closeParams.TextDocument.Uri); + if (closedFile == null) + { + return; + } + + // Trash the existing document from our mapping + Workspace.CloseFile(closedFile); + + // Send out a notification to other services that have subscribed to this event + var textDocClosedTasks = TextDocCloseCallbacks.Select(t => t(closedFile, eventContext)); + await Task.WhenAll(textDocClosedTasks); + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // Swallow exceptions here to prevent us from crashing + // TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here return; } - - // Skip closing this file if the file doesn't exist - var closedFile = Workspace.GetFile(closeParams.TextDocument.Uri); - if (closedFile == null) - { - return; - } - - // Trash the existing document from our mapping - Workspace.CloseFile(closedFile); - - // Send out a notification to other services that have subscribed to this event - var textDocClosedTasks = TextDocCloseCallbacks.Select(t => t(closedFile, eventContext)); - await Task.WhenAll(textDocClosedTasks); } /// @@ -297,12 +318,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace DidChangeConfigurationParams configChangeParams, EventContext eventContext) { - Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification"); + try + { + Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification"); - // Propagate the changes to the event handlers - var configUpdateTasks = ConfigChangeCallbacks.Select( - t => t(configChangeParams.Settings, CurrentSettings, eventContext)); - await Task.WhenAll(configUpdateTasks); + // Propagate the changes to the event handlers + var configUpdateTasks = ConfigChangeCallbacks.Select( + t => t(configChangeParams.Settings, CurrentSettings, eventContext)); + await Task.WhenAll(configUpdateTasks); + } + catch (Exception ex) + { + Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString()); + // Swallow exceptions here to prevent us from crashing + // TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here + return; + } } #endregion diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Workspace/WorkspaceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Workspace/WorkspaceTests.cs index fe3e4b9b..92e365ef 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Workspace/WorkspaceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Workspace/WorkspaceTests.cs @@ -82,10 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Workspace TextDocument = new TextDocumentItem {Uri = TestObjects.ScriptUri} }; // Then: - // ... There should be a file not found exception thrown - // TODO: This logic should be changed to not create the ScriptFile - await Assert.ThrowsAnyAsync( - () => workspaceService.HandleDidCloseTextDocumentNotification(requestParams, eventContext)); + await workspaceService.HandleDidCloseTextDocumentNotification(requestParams, eventContext); // ... There should still be no open files // ... The callback should not have been called