diff --git a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs index b865179f..50f6baa9 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs @@ -1,229 +1,229 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.SqlTools.Extensibility; -using Microsoft.SqlTools.Hosting; -using Microsoft.SqlTools.Hosting.Contracts; -using Microsoft.SqlTools.Hosting.Protocol; -using Microsoft.SqlTools.Hosting.Protocol.Channel; -using Microsoft.SqlTools.Utility; -using Microsoft.SqlTools.ServiceLayer.Connection; -using Microsoft.SqlTools.ServiceLayer.Admin; - -namespace Microsoft.SqlTools.ServiceLayer.Hosting -{ - /// - /// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC - /// implementation for sending/receiving JSON requests and dispatching the requests to - /// handlers that are registered prior to startup. - /// - public sealed class ServiceHost : ServiceHostBase - { - public const string ProviderName = "MSSQL"; - private const string ProviderDescription = "Microsoft SQL Server"; - private const string ProviderProtocolVersion = "1.0"; - - /// - /// This timeout limits the amount of time that shutdown tasks can take to complete - /// prior to the process shutting down. - /// - private const int ShutdownTimeoutInSeconds = 120; - public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" }; - private IMultiServiceProvider serviceProvider; - - #region Singleton Instance Code - - /// - /// Singleton instance of the service host for internal storage - /// - private static readonly Lazy instance = new Lazy(() => new ServiceHost()); - - /// - /// Current instance of the ServiceHost - /// - public static ServiceHost Instance - { - get { return instance.Value; } - } - - /// - /// Constructs new instance of ServiceHost using the host and profile details provided. - /// Access is private to ensure only one instance exists at a time. - /// - private ServiceHost() : base(new StdioServerChannel()) - { - // Initialize the shutdown activities - shutdownCallbacks = new List(); - initializeCallbacks = new List(); - } - - public IMultiServiceProvider ServiceProvider - { - get - { - return serviceProvider; - } - internal set - { - serviceProvider = value; - } - } - - /// - /// Provide initialization that must occur after the service host is started - /// - public void InitializeRequestHandlers() - { - // Register the requests that this service host will handle - this.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequest); - this.SetRequestHandler(CapabilitiesRequest.Type, HandleCapabilitiesRequest); - this.SetRequestHandler(ShutdownRequest.Type, HandleShutdownRequest); - this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest); - } - - #endregion - - #region Member Variables - - /// - /// Delegate definition for the host shutdown event - /// - /// - /// - public delegate Task ShutdownCallback(object shutdownParams, RequestContext shutdownRequestContext); - - /// - /// Delegate definition for the host initialization event - /// - /// - /// - public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext requestContext); - - private readonly List shutdownCallbacks; - - private readonly List initializeCallbacks; - - private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version; - - #endregion - - #region Public Methods - - /// - /// Adds a new callback to be called when the shutdown request is submitted - /// - /// Callback to perform when a shutdown request is submitted - public void RegisterShutdownTask(ShutdownCallback callback) - { - shutdownCallbacks.Add(callback); - } - - /// - /// Add a new method to be called when the initialize request is submitted - /// - /// Callback to perform when an initialize request is submitted - public void RegisterInitializeTask(InitializeCallback callback) - { - initializeCallbacks.Add(callback); - } - - #endregion - - #region Request Handlers - - /// - /// Handles the shutdown event for the Language Server - /// - private async Task HandleShutdownRequest(object shutdownParams, RequestContext requestContext) - { - Logger.Write(LogLevel.Normal, "Service host is shutting down..."); - - // Call all the shutdown methods provided by the service components - Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray(); - TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds); - // shut down once all tasks are completed, or after the timeout expires, whichever comes first. - await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0)); - } - - /// - /// Handles the initialization request - /// - /// - /// - /// - internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext requestContext) - { - // Call all tasks that registered on the initialize request - var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext)); - await Task.WhenAll(initializeTasks); - - // TODO: Figure out where this needs to go to be agnostic of the language - - // Send back what this server can do - await requestContext.SendResult( - new InitializeResult - { - Capabilities = new ServerCapabilities - { - TextDocumentSync = TextDocumentSyncKind.Incremental, - DefinitionProvider = true, - ReferencesProvider = false, - DocumentFormattingProvider = true, - DocumentRangeFormattingProvider = true, - DocumentHighlightProvider = false, - HoverProvider = true, - CompletionProvider = new CompletionOptions - { - ResolveProvider = true, - TriggerCharacters = CompletionTriggerCharacters - }, - SignatureHelpProvider = new SignatureHelpOptions - { - TriggerCharacters = new string[] { " ", "," } - } - } - }); - } - - /// - /// Handles a request for the capabilities request - /// - internal async Task HandleCapabilitiesRequest( - CapabilitiesRequest initializeParams, - RequestContext requestContext) - { - await requestContext.SendResult( - new CapabilitiesResult - { - Capabilities = new DmpServerCapabilities - { - ProtocolVersion = ServiceHost.ProviderProtocolVersion, - ProviderName = ServiceHost.ProviderName, - ProviderDisplayName = ServiceHost.ProviderDescription, - ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(), - AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions() - } - } - ); - } - - /// - /// Handles the version request. Sends back the server version as result. - /// - private static async Task HandleVersionRequest( - object versionRequestParams, - RequestContext requestContext) - { - await requestContext.SendResult(serviceVersion.ToString()); - } - - #endregion - } -} +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.SqlTools.Extensibility; +using Microsoft.SqlTools.Hosting; +using Microsoft.SqlTools.Hosting.Contracts; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.Hosting.Protocol.Channel; +using Microsoft.SqlTools.Utility; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Admin; + +namespace Microsoft.SqlTools.ServiceLayer.Hosting +{ + /// + /// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC + /// implementation for sending/receiving JSON requests and dispatching the requests to + /// handlers that are registered prior to startup. + /// + public sealed class ServiceHost : ServiceHostBase + { + public const string ProviderName = "MSSQL"; + private const string ProviderDescription = "Microsoft SQL Server"; + private const string ProviderProtocolVersion = "1.0"; + + /// + /// This timeout limits the amount of time that shutdown tasks can take to complete + /// prior to the process shutting down. + /// + private const int ShutdownTimeoutInSeconds = 120; + public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" }; + private IMultiServiceProvider serviceProvider; + + #region Singleton Instance Code + + /// + /// Singleton instance of the service host for internal storage + /// + private static readonly Lazy instance = new Lazy(() => new ServiceHost()); + + /// + /// Current instance of the ServiceHost + /// + public static ServiceHost Instance + { + get { return instance.Value; } + } + + /// + /// Constructs new instance of ServiceHost using the host and profile details provided. + /// Access is private to ensure only one instance exists at a time. + /// + private ServiceHost() : base(new StdioServerChannel()) + { + // Initialize the shutdown activities + shutdownCallbacks = new List(); + initializeCallbacks = new List(); + } + + public IMultiServiceProvider ServiceProvider + { + get + { + return serviceProvider; + } + internal set + { + serviceProvider = value; + } + } + + /// + /// Provide initialization that must occur after the service host is started + /// + public void InitializeRequestHandlers() + { + // Register the requests that this service host will handle + this.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequest); + this.SetRequestHandler(CapabilitiesRequest.Type, HandleCapabilitiesRequest); + this.SetRequestHandler(ShutdownRequest.Type, HandleShutdownRequest); + this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest); + } + + #endregion + + #region Member Variables + + /// + /// Delegate definition for the host shutdown event + /// + /// + /// + public delegate Task ShutdownCallback(object shutdownParams, RequestContext shutdownRequestContext); + + /// + /// Delegate definition for the host initialization event + /// + /// + /// + public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext requestContext); + + private readonly List shutdownCallbacks; + + private readonly List initializeCallbacks; + + private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version; + + #endregion + + #region Public Methods + + /// + /// Adds a new callback to be called when the shutdown request is submitted + /// + /// Callback to perform when a shutdown request is submitted + public void RegisterShutdownTask(ShutdownCallback callback) + { + shutdownCallbacks.Add(callback); + } + + /// + /// Add a new method to be called when the initialize request is submitted + /// + /// Callback to perform when an initialize request is submitted + public void RegisterInitializeTask(InitializeCallback callback) + { + initializeCallbacks.Add(callback); + } + + #endregion + + #region Request Handlers + + /// + /// Handles the shutdown event for the Language Server + /// + private async Task HandleShutdownRequest(object shutdownParams, RequestContext requestContext) + { + Logger.Write(LogLevel.Normal, "Service host is shutting down..."); + + // Call all the shutdown methods provided by the service components + Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray(); + TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds); + // shut down once all tasks are completed, or after the timeout expires, whichever comes first. + await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0)); + } + + /// + /// Handles the initialization request + /// + /// + /// + /// + internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext requestContext) + { + // Call all tasks that registered on the initialize request + var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext)); + await Task.WhenAll(initializeTasks); + + // TODO: Figure out where this needs to go to be agnostic of the language + + // Send back what this server can do + await requestContext.SendResult( + new InitializeResult + { + Capabilities = new ServerCapabilities + { + TextDocumentSync = TextDocumentSyncKind.Incremental, + DefinitionProvider = true, + ReferencesProvider = false, + DocumentFormattingProvider = true, + DocumentRangeFormattingProvider = true, + DocumentHighlightProvider = false, + HoverProvider = true, + CompletionProvider = new CompletionOptions + { + ResolveProvider = true, + TriggerCharacters = CompletionTriggerCharacters + }, + SignatureHelpProvider = new SignatureHelpOptions + { + TriggerCharacters = new string[] { " ", "," } + } + } + }); + } + + /// + /// Handles a request for the capabilities request + /// + internal async Task HandleCapabilitiesRequest( + CapabilitiesRequest initializeParams, + RequestContext requestContext) + { + await requestContext.SendResult( + new CapabilitiesResult + { + Capabilities = new DmpServerCapabilities + { + ProtocolVersion = ServiceHost.ProviderProtocolVersion, + ProviderName = ServiceHost.ProviderName, + ProviderDisplayName = ServiceHost.ProviderDescription, + ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(), + AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions() + } + } + ); + } + + /// + /// Handles the version request. Sends back the server version as result. + /// + private static async Task HandleVersionRequest( + object versionRequestParams, + RequestContext requestContext) + { + await requestContext.SendResult(serviceVersion.ToString()); + } + + #endregion + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskInfo.cs index 2c1a4bea..570efba4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskInfo.cs @@ -4,6 +4,8 @@ // +using Microsoft.SqlTools.ServiceLayer.Hosting; + namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts { public class TaskInfo @@ -34,6 +36,17 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts /// public string Name { get; set; } + /// + /// Provider Name + /// + public string ProviderName + { + get + { + return ServiceHost.ProviderName; + } + } + /// /// Task description /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskNotifications.cs b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskNotifications.cs index a900093e..b4eb4dea 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskNotifications.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/Contracts/TaskNotifications.cs @@ -14,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts { public static readonly EventType Type = - EventType.Create("task/newtaskcreated"); + EventType.Create("tasks/newtaskcreated"); } /// @@ -24,6 +24,6 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts { public static readonly EventType Type = - EventType.Create("task/statuschanged"); + EventType.Create("tasks/statuschanged"); } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/SqlTask.cs b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/SqlTask.cs index 0188ebd2..54988ec6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/SqlTask.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/SqlTask.cs @@ -20,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices public class SqlTask : IDisposable { private bool isCompleted; - private bool isCanceled; + private bool isCancelRequested; private bool isDisposed; private readonly object lockObject = new object(); private readonly List messages = new List(); @@ -114,18 +114,18 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices /// Setting this to True will not change the Slot status. /// Setting the Slot status to Canceled will set this to true. /// - public bool IsCanceled + public bool IsCancelRequested { get { - return isCanceled; + return isCancelRequested; } private set { - if (isCanceled != value) + if (isCancelRequested != value) { - isCanceled = value; - OnTaskCanceled(); + isCancelRequested = value; + OnTaskCancelRequested(); } } } @@ -221,9 +221,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices throw new NotSupportedException("IsCompleted is not determined for status: " + status); } - if (status == SqlTaskStatus.Canceled) + if (status == SqlTaskStatus.Canceled && !isCancelRequested) { - IsCanceled = true; + IsCancelRequested = true; } OnStatusChanged(); @@ -251,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices /// public void Cancel() { - IsCanceled = true; + IsCancelRequested = true; } /// @@ -308,11 +308,11 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices { return new TaskInfo { + TaskId = this.TaskId.ToString(), DatabaseName = TaskMetadata.DatabaseName, ServerName = TaskMetadata.ServerName, Name = TaskMetadata.Name, Description = TaskMetadata.Description, - TaskId = TaskId.ToString() }; } @@ -366,7 +366,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices } } - private void OnTaskCanceled() + private void OnTaskCancelRequested() { var handler = TaskCanceled; if (handler != null) diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/SqlTaskTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/SqlTaskTests.cs index 3b7ccfbe..e35031a3 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/SqlTaskTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/SqlTaskTests.cs @@ -109,7 +109,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices sqlTask.Run().ContinueWith(task => { Assert.Equal(sqlTask.TaskStatus, expectedStatus); - Assert.Equal(sqlTask.IsCanceled, true); + Assert.Equal(sqlTask.IsCancelRequested, true); Assert.True(sqlTask.Duration > 0); }); Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress); @@ -129,7 +129,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices sqlTask.Run().ContinueWith(task => { Assert.Equal(sqlTask.TaskStatus, expectedStatus); - Assert.Equal(sqlTask.IsCanceled, true); + Assert.Equal(sqlTask.IsCancelRequested, true); Assert.True(sqlTask.Duration > 0); }); Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress); diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskManagerTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskManagerTests.cs index a2bb04c4..a64cdc42 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskManagerTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskManagerTests.cs @@ -74,7 +74,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices sqlTask.Run().ContinueWith(task => { Assert.Equal(sqlTask.TaskStatus, expectedStatus); - Assert.Equal(sqlTask.IsCanceled, true); + Assert.Equal(sqlTask.IsCancelRequested, true); manager.Reset(); }); diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskServiceTests.cs index 8b0fb389..dcb4ebb2 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/TaskServices/TaskServiceTests.cs @@ -57,7 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices }); serviceHostMock.Verify(x => x.SendEvent(TaskCreatedNotification.Type, - It.Is(t => t.TaskId == sqlTask.TaskId.ToString())), Times.Once()); + It.Is(t => t.TaskId == sqlTask.TaskId.ToString() && t.ProviderName == "MSSQL")), Times.Once()); operation.Stop(); serviceHostMock.Verify(x => x.SendEvent(TaskStatusChangedNotification.Type,