diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/CreateSessionRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/CreateSessionRequest.cs
index 51226097..3fa4d541 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/CreateSessionRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/CreateSessionRequest.cs
@@ -1,45 +1,76 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
-using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
-
-namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
-{
- ///
- /// Information returned from a .
- /// Contains success information, a to be used when
- /// requesting expansion of nodes, and a root node to display for this area.
- ///
- public class CreateSessionResponse
- {
- ///
- /// Boolean indicating if the connection was successful
- ///
- public bool Success { get; set; }
-
- ///
- /// Unique ID to use when sending any requests for objects in the
- /// tree under the node
- ///
- public string SessionId { get; set; }
-
- ///
- /// Information describing the base node in the tree
- ///
- public NodeInfo RootNode { get; set; }
- }
- ///
- /// Establishes an Object Explorer tree session for a specific connection.
- /// This will create a connection to a specific server or database, register
- /// it for use in the
- ///
- public class CreateSessionRequest
- {
- public static readonly
- RequestType Type =
- RequestType.Create("objectexplorer/createsession");
- }
-}
+using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
+{
+ ///
+ /// Information returned from a .
+ /// Contains success information, a to be used when
+ /// requesting expansion of nodes, and a root node to display for this area.
+ ///
+ public class CreateSessionResponse
+ {
+ ///
+ /// Unique ID to use when sending any requests for objects in the
+ /// tree under the node
+ ///
+ public string SessionId { get; set; }
+
+ }
+
+ ///
+ /// Information returned from a .
+ /// Contains success information, a to be used when
+ /// requesting expansion of nodes, and a root node to display for this area.
+ ///
+ public class SessionCreatedParameters
+ {
+ ///
+ /// Boolean indicating if the connection was successful
+ ///
+ public bool Success { get; set; }
+
+ ///
+ /// Unique ID to use when sending any requests for objects in the
+ /// tree under the node
+ ///
+ public string SessionId { get; set; }
+
+ ///
+ /// Information describing the base node in the tree
+ ///
+ public NodeInfo RootNode { get; set; }
+
+
+ ///
+ /// Error message returned from the engine for a object explorer session failure reason, if any.
+ ///
+ public string ErrorMessage { get; set; }
+ }
+ ///
+ /// Establishes an Object Explorer tree session for a specific connection.
+ /// This will create a connection to a specific server or database, register
+ /// it for use in the
+ ///
+ public class CreateSessionRequest
+ {
+ public static readonly
+ RequestType Type =
+ RequestType.Create("objectexplorer/createsession");
+ }
+
+ ///
+ /// Session notification mapping entry
+ ///
+ public class CreateSessionCompleteNotification
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("objectexplorer/sessionCreated");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs
index a004d172..3db9f926 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs
@@ -1,56 +1,76 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
-
-namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
+
+namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
{
- ///
- /// Information returned from a .
- ///
+ ///
+ /// Information returned from a .
+ ///
public class ExpandResponse
{
- ///
- /// Unique ID to use when sending any requests for objects in the
- /// tree under the node
- ///
+ ///
+ /// Unique ID to use when sending any requests for objects in the
+ /// tree under the node
+ ///
public string SessionId { get; set; }
- ///
- /// Information describing the expanded nodes in the tree
- ///
+ ///
+ /// Information describing the expanded nodes in the tree
+ ///
public NodeInfo[] Nodes { get; set; }
- }
-
- ///
- /// Parameters to the .
- ///
- public class ExpandParams
- {
- ///
- /// The Id returned from a . This
- /// is used to disambiguate between different trees.
- ///
- public string SessionId { get; set; }
-
- ///
- /// Path identifying the node to expand. See for details
- ///
- public string NodePath { get; set; }
- }
-
- ///
- /// A request to expand a
- ///
- public class ExpandRequest
- {
- ///
- /// Returns children of a given node as a array.
- ///
- public static readonly
- RequestType Type =
- RequestType.Create("objectexplorer/expand");
- }
-}
+
+ ///
+ /// Path identifying the node to expand. See for details
+ ///
+ public string NodePath { get; set; }
+
+ ///
+ /// Error message returned from the engine for a object explorer expand failure reason, if any.
+ ///
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// Parameters to the .
+ ///
+ public class ExpandParams
+ {
+ ///
+ /// The Id returned from a . This
+ /// is used to disambiguate between different trees.
+ ///
+ public string SessionId { get; set; }
+
+ ///
+ /// Path identifying the node to expand. See for details
+ ///
+ public string NodePath { get; set; }
+ }
+
+ ///
+ /// A request to expand a
+ ///
+ public class ExpandRequest
+ {
+ ///
+ /// Returns children of a given node as a array.
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("objectexplorer/expand");
+ }
+
+ ///
+ /// Expand notification mapping entry
+ ///
+ public class ExpandCompleteNotification
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("objectexplorer/expandCompleted");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/NodeInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/NodeInfo.cs
index 55246bdc..c276fe08 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/NodeInfo.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/NodeInfo.cs
@@ -53,5 +53,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
/// Object Metadata for smo objects to be used for scripting
///
public ObjectMetadata Metadata { get; set; }
+
+ ///
+ /// Error message returned from the engine for a object explorer node failure reason, if any.
+ ///
+ public string ErrorMessage { get; set; }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs
index c9974c6f..15fb4c20 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs
@@ -7,38 +7,11 @@ using Microsoft.SqlTools.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
{
- ///
- /// Information returned from a .
- ///
- public class RefreshResponse
- {
- ///
- /// Unique ID to use when sending any requests for objects in the
- /// tree under the node
- ///
- public string SessionId { get; set; }
-
- ///
- /// Information describing the expanded nodes in the tree
- ///
- public NodeInfo[] Nodes { get; set; }
- }
-
///
/// Parameters to the .
///
- public class RefreshParams
+ public class RefreshParams: ExpandParams
{
- ///
- /// The Id returned from a . This
- /// is used to disambiguate between different trees.
- ///
- public string SessionId { get; set; }
-
- ///
- /// Path identifying the node to refresh. See for details
- ///
- public string NodePath { get; set; }
}
///
@@ -50,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts
/// Returns children of a given node as a array.
///
public static readonly
- RequestType Type =
- RequestType.Create("objectexplorer/refresh");
+ RequestType Type =
+ RequestType.Create("objectexplorer/refresh");
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
index b00cde92..5c208e40 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
@@ -77,6 +77,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
///
public string NodeSubType { get; set; }
+ ///
+ /// Error message returned from the engine for a object explorer node failure reason, if any.
+ ///
+ public string ErrorMessage { get; set; }
+
///
/// Node status - for example login can be disabled/enabled
///
@@ -193,7 +198,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
NodeType = this.NodeType,
Metadata = this.ObjectMetadata,
NodeStatus = this.NodeStatus,
- NodeSubType = this.NodeSubType
+ NodeSubType = this.NodeSubType,
+ ErrorMessage = this.ErrorMessage
};
}
@@ -289,8 +295,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
bool includeSystemObjects = context != null && context.Database != null ? ObjectExplorerUtils.IsSystemDatabaseConnection(context.Database.Name) : true;
- if (children.IsPopulating || context == null)
- return;
+ if (children.IsPopulating || context == null)
+ return;
children.Clear();
BeginChildrenInit();
@@ -302,16 +308,26 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
{
foreach (var factory in childFactories)
{
- IEnumerable items = factory.Expand(this, refresh, name, includeSystemObjects);
- if (items != null)
+ try
{
- foreach (TreeNode item in items)
+ IEnumerable items = factory.Expand(this, refresh, name, includeSystemObjects);
+ if (items != null)
{
- children.Add(item);
- item.Parent = this;
+ foreach (TreeNode item in items)
+ {
+ children.Add(item);
+ item.Parent = this;
+ }
}
}
+ catch (Exception ex)
+ {
+ string error = string.Format(CultureInfo.InvariantCulture, "Failed populating oe children. error:{0} inner:{1} stacktrace:{2}",
+ ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
+ Logger.Write(LogLevel.Error, error);
+ ErrorMessage = ex.Message;
+ }
}
}
}
@@ -320,6 +336,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
string error = string.Format(CultureInfo.InvariantCulture, "Failed populating oe children. error:{0} inner:{1} stacktrace:{2}",
ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
Logger.Write(LogLevel.Error, error);
+ ErrorMessage = ex.Message;
}
finally
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
index 7497add6..9f4bc96c 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
@@ -103,12 +103,130 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
serviceHost.SetRequestHandler(CloseSessionRequest.Type, HandleCloseSessionRequest);
}
- public void CloseSession(string uri)
+
+
+ internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext context)
+ {
+ Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest");
+
+ Func> doCreateSession = async () =>
+ {
+ Validate.IsNotNull(nameof(connectionDetails), connectionDetails);
+ Validate.IsNotNull(nameof(context), context);
+ return await Task.Factory.StartNew(() =>
+ {
+ string uri = GenerateUri(connectionDetails);
+
+ return new CreateSessionResponse { SessionId = uri };
+ });
+ };
+
+ CreateSessionResponse response = await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest");
+ if (response != null)
+ {
+ RunCreateSessionTask(connectionDetails, response.SessionId);
+ }
+
+ }
+
+ internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext context)
+ {
+ Logger.Write(LogLevel.Verbose, "HandleExpandRequest");
+
+ Func> expandNode = async () =>
+ {
+ Validate.IsNotNull(nameof(expandParams), expandParams);
+ Validate.IsNotNull(nameof(context), context);
+
+ string uri = expandParams.SessionId;
+ ObjectExplorerSession session = null;
+ NodeInfo[] nodes = null;
+ ExpandResponse response;
+ 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
+ {
+ SessionId = expandParams.SessionId,
+ NodePath = expandParams.NodePath,
+ ErrorMessage = $"Couldn't find session for session: {uri}"
+ });
+ return false;
+ }
+ else
+ {
+ RunExpandTask(session, expandParams);
+ return true;
+ }
+ };
+ 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);
+
+ string uri = refreshParams.SessionId;
+ ObjectExplorerSession session = null;
+ ExpandResponse response;
+ 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
+ {
+ SessionId = refreshParams.SessionId,
+ NodePath = refreshParams.NodePath,
+ ErrorMessage = $"Couldn't find session for session: {uri}"
+ });
+ }
+ else
+ {
+ RunExpandTask(session, refreshParams, true);
+ }
+ await context.SendResult(true);
+ }
+
+ internal async Task HandleCloseSessionRequest(CloseSessionParams closeSessionParams, RequestContext context)
+ {
+
+ Logger.Write(LogLevel.Verbose, "HandleCloseSessionRequest");
+ Func> closeSession = () =>
+ {
+ Validate.IsNotNull(nameof(closeSessionParams), closeSessionParams);
+ Validate.IsNotNull(nameof(context), context);
+ return Task.Factory.StartNew(() =>
+ {
+ string uri = closeSessionParams.SessionId;
+ ObjectExplorerSession session = null;
+ bool success = false;
+ if (!sessionMap.TryGetValue(uri, out session))
+ {
+ Logger.Write(LogLevel.Verbose, $"Cannot close object explorer session. Couldn't find session for uri. {uri} ");
+ }
+
+ if (session != null)
+ {
+ // refresh the nodes for given node path
+ CloseSession(uri);
+ success = true;
+ }
+
+ var response = new CloseSessionResponse() { Success = success, SessionId = uri };
+ return response;
+ });
+ };
+
+ await HandleRequestAsync(closeSession, context, "HandleCloseSessionRequest");
+ }
+
+ internal void CloseSession(string uri)
{
ObjectExplorerSession session;
if (sessionMap.TryGetValue(uri, out session))
{
- // Establish a connection to the specified server/database
+ // Remove the session from active sessions and disconnect
sessionMap.TryRemove(session.Uri, out session);
connectionService.Disconnect(new DisconnectParams()
{
@@ -116,43 +234,52 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
});
}
}
-
- internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext context)
+
+ private void RunCreateSessionTask(ConnectionDetails connectionDetails, string uri)
{
- Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest");
- Func> doCreateSession = async () =>
+ if (connectionDetails != null && !string.IsNullOrEmpty(uri))
{
- Validate.IsNotNull(nameof(connectionDetails), connectionDetails);
- Validate.IsNotNull(nameof(context), context);
+ Task task = CreateSessionAsync(connectionDetails, uri);
+ CreateSessionTask = task;
+ Task.Run(async () => await task);
+ }
+ }
- string uri = GenerateUri(connectionDetails);
+ ///
+ /// For tests only
+ ///
+ internal Task CreateSessionTask
+ {
+ get;
+ private set;
+ }
- ObjectExplorerSession session;
- if (!sessionMap.TryGetValue(uri, out session))
- {
- // Establish a connection to the specified server/database
- session = await DoCreateSession(connectionDetails, uri);
- }
+ private async Task CreateSessionAsync(ConnectionDetails connectionDetails, string uri)
+ {
+ ObjectExplorerSession session;
+ if (!sessionMap.TryGetValue(uri, out session))
+ {
+ // Establish a connection to the specified server/database
+ session = await DoCreateSession(connectionDetails, uri);
+ }
- CreateSessionResponse response;
- if (session == null)
+ SessionCreatedParameters response;
+ if (session != null)
+ {
+ // Else we have a session available, response with existing session information
+ response = new SessionCreatedParameters
{
- response = new CreateSessionResponse() { Success = false };
- }
- else
- {
- // Else we have a session available, response with existing session information
- response = new CreateSessionResponse()
- {
- Success = true,
- RootNode = session.Root.ToNodeInfo(),
- SessionId = session.Uri
- };
- }
+ Success = true,
+ RootNode = session.Root.ToNodeInfo(),
+ SessionId = uri,
+ ErrorMessage = session.ErrorMessage
+
+ };
+ await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, response);
return response;
- };
+ }
+ return null;
- await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest");
}
internal async Task ExpandNode(ObjectExplorerSession session, string nodePath, bool forceRefresh = false)
@@ -186,9 +313,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
connectionDetails.PersistSecurityInfo = true;
ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails };
- ConnectionCompleteParams connectionResult = await Connect(connectParams);
+ ConnectionCompleteParams connectionResult = await Connect(connectParams, uri);
if (connectionResult == null)
{
+ // Connection failed and notification is already sent
return null;
}
@@ -198,7 +326,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
}
- private async Task Connect(ConnectParams connectParams)
+ private async Task Connect(ConnectParams connectParams, string uri)
{
string connectionErrorMessage = string.Empty;
try
@@ -213,7 +341,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
else
{
Logger.Write(LogLevel.Warning, $"Connection Failed for OE. connection error: {connectionErrorMessage}");
- await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
+ await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, new SessionCreatedParameters
+ {
+ ErrorMessage = result.ErrorMessage,
+ SessionId = uri
+ });
return null;
}
@@ -222,108 +354,46 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
{
Logger.Write(LogLevel.Warning, $"Connection Failed for OE. connection error:{connectionErrorMessage} error: {ex.Message}");
// Send a connection failed error message in this case.
- ConnectionCompleteParams result = new ConnectionCompleteParams()
+ SessionCreatedParameters result = new SessionCreatedParameters()
{
- Messages = ex.ToString()
+ ErrorMessage = ex.ToString(),
+ SessionId = uri
};
- await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
+ await serviceHost.SendEvent(CreateSessionCompleteNotification.Type, result);
return null;
}
}
- internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext context)
+ private void RunExpandTask(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
{
- Logger.Write(LogLevel.Verbose, "HandleExpandRequest");
- Func> expandNode = async () =>
+ Task task = ExpandNodeAsync(session, expandParams, forceRefresh);
+ ExpandTask = task;
+ Task.Run(async () =>
{
- Validate.IsNotNull(nameof(expandParams), expandParams);
- Validate.IsNotNull(nameof(context), context);
-
- string uri = expandParams.SessionId;
- ObjectExplorerSession session = null;
- NodeInfo[] nodes = null;
- ExpandResponse response;
- if (!sessionMap.TryGetValue(uri, out session))
- {
- Logger.Write(LogLevel.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
- }
-
- if (session != null)
- {
- // expand the nodes for given node path
- nodes = await ExpandNode(session, expandParams.NodePath);
- }
-
- response = new ExpandResponse() { Nodes = nodes, SessionId = uri };
- return response;
- };
-
- await HandleRequestAsync(expandNode, context, "HandleExpandRequest");
+ await task;
+ });
}
- internal async Task HandleRefreshRequest(RefreshParams refreshParams, RequestContext context)
+ ///
+ /// For tests only
+ ///
+ internal Task ExpandTask
{
- Logger.Write(LogLevel.Verbose, "HandleRefreshRequest");
- Func> refreshNode = async () =>
- {
- Validate.IsNotNull(nameof(refreshParams), refreshParams);
- Validate.IsNotNull(nameof(context), context);
-
- string uri = refreshParams.SessionId;
- ObjectExplorerSession session = null;
- NodeInfo[] nodes = null;
- RefreshResponse response;
- if (!sessionMap.TryGetValue(uri, out session))
- {
- Logger.Write(LogLevel.Verbose, $"Cannot refresh object explorer node. Couldn't find session for uri. {uri} ");
- }
-
- if (session != null)
- {
- // refresh the nodes for given node path
- nodes = await ExpandNode(session, refreshParams.NodePath, true);
- }
-
- response = new RefreshResponse() { Nodes = nodes, SessionId = uri };
- return response;
- };
-
- await HandleRequestAsync(refreshNode, context, "HandleRefreshRequest");
+ get;
+ set;
}
- internal async Task HandleCloseSessionRequest(CloseSessionParams closeSessionParams, RequestContext context)
+ private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
{
- Validate.IsNotNull(nameof(closeSessionParams), closeSessionParams);
- Validate.IsNotNull(nameof(context), context);
- Logger.Write(LogLevel.Verbose, "HandleCloseSessionRequest");
- Func> closeSession = () =>
- {
- return Task.Factory.StartNew(() =>
- {
- string uri = closeSessionParams.SessionId;
- ObjectExplorerSession session = null;
- bool success = false;
- if (!sessionMap.TryGetValue(uri, out session))
- {
- Logger.Write(LogLevel.Verbose, $"Cannot close object explorer session. Couldn't find session for uri. {uri} ");
- }
-
- if (session != null)
- {
- // refresh the nodes for given node path
- CloseSession(uri);
- success = true;
- }
-
- var response = new CloseSessionResponse() {Success = success, SessionId = uri};
- return response;
- });
- };
-
- await HandleRequestAsync(closeSession, context, "HandleCloseSessionRequest");
+ NodeInfo[] nodes = null;
+ nodes = await ExpandNode(session, expandParams.NodePath, forceRefresh);
+ ExpandResponse response = new ExpandResponse() { Nodes = nodes, SessionId = session.Uri, NodePath = expandParams.NodePath };
+ await serviceHost.SendEvent(ExpandCompleteNotification.Type, response);
}
- private async Task HandleRequestAsync(Func> handler, RequestContext requestContext, string requestType)
+
+
+ private async Task HandleRequestAsync(Func> handler, RequestContext requestContext, string requestType)
{
Logger.Write(LogLevel.Verbose, requestType);
@@ -331,11 +401,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
{
T result = await handler();
await requestContext.SendResult(result);
+ return result;
}
catch (Exception ex)
{
await requestContext.SendError(ex.ToString());
}
+ return default(T);
}
///
@@ -441,21 +513,19 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
public string Uri { get; private set; }
public TreeNode Root { get; private set; }
+ public string ErrorMessage { get; set; }
+
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider)
{
- TreeNode rootNode = new ServerNode(response, serviceProvider);
+ ServerNode rootNode = new ServerNode(response, serviceProvider);
var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService());
if (!ObjectExplorerUtils.IsSystemDatabaseConnection(response.ConnectionSummary.DatabaseName))
{
// Assuming the databases are in a folder under server node
- var children = rootNode.Expand();
- var databasesRoot = children.FirstOrDefault(x => x.NodeTypeId == NodeTypes.Databases);
- var databasesChildren = databasesRoot.Expand(response.ConnectionSummary.DatabaseName);
- var databases = databasesChildren.Where(x => x.NodeType == NodeTypes.Database.ToString());
- var databaseNode = databases.FirstOrDefault(d => d.Label == response.ConnectionSummary.DatabaseName);
- databaseNode.Label = rootNode.Label;
+ DatabaseTreeNode databaseNode = new DatabaseTreeNode(rootNode, response.ConnectionSummary.DatabaseName);
session.Root = databaseNode;
}
+
return session;
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
index d31c5db7..bd12bcaf 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
@@ -10,6 +10,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
{
internal partial class DatabaseTreeNode
{
+ public DatabaseTreeNode(ServerNode serverNode, string databaseName): this()
+ {
+ Parent = serverNode;
+ NodeValue = databaseName;
+ Database db = new Database(serverNode.GetContextAs().Server, this.NodeValue);
+ db.Refresh();
+ CacheInfoFromModel(db);
+ }
///
/// Initializes the context and ensures that
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs
index d22c5720..839bffe1 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs
@@ -42,6 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
string error = string.Format(CultureInfo.InvariantCulture, "Failed expanding oe children. parent:{0} error:{1} inner:{2} stacktrace:{3}",
parent != null ? parent.GetNodePath() : "", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
Logger.Write(LogLevel.Error, error);
+ throw ex;
}
finally
{
@@ -97,7 +98,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
string propertyFilter = GetProperyFilter(filters, querier.GetType(), validForFlag);
try
{
- foreach (var smoObject in querier.Query(context, propertyFilter, refresh))
+ var smoObjectList = querier.Query(context, propertyFilter, refresh).ToList();
+ foreach (var smoObject in smoObjectList)
{
if (smoObject == null)
{
@@ -116,6 +118,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
string error = string.Format(CultureInfo.InvariantCulture, "Failed getting smo objects. parent:{0} querier: {1} error:{2} inner:{3} stacktrace:{4}",
parent != null ? parent.GetNodePath() : "", querier.GetType(), ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
Logger.Write(LogLevel.Error, error);
+ throw ex;
}
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQuerier.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQuerier.cs
index 79004da3..05d64440 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQuerier.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQuerier.cs
@@ -83,7 +83,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
string urn = string.Empty;
try
{
- string parentUrn = smoObject.Urn;
+ string parentUrn = smoObject.Urn != null ? smoObject.Urn.Value : string.Empty;
urn = parentUrn != null ? $"{parentUrn.ToString()}/{objectName}" + filter : string.Empty;
if (!string.IsNullOrEmpty(urn))
@@ -102,6 +102,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
string error = string.Format(CultureInfo.InvariantCulture, "Failed getting urns. error:{0} inner:{1} stacktrace:{2}",
ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
Logger.Write(LogLevel.Error, error);
+ throw ex;
}
return urns;
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs
index 3bf70273..fc8bd2bd 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs
@@ -50,8 +50,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
{
// Given the connection service fails to connect
ConnectionDetails details = TestObjects.GetTestConnectionDetails();
- ConnectionCompleteParams completeParams = null;
- serviceHostMock.AddEventHandling(ConnectionCompleteNotification.Type, (et, p) => completeParams = p);
string expectedExceptionText = "Error!!!";
connectionServiceMock.Setup(c => c.Connect(It.IsAny()))
@@ -59,21 +57,19 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when creating a new session
// then expect the create session request to return false
- await RunAndVerify(
- test: (requestContext) => service.HandleCreateSessionRequest(details, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallCreateSession(details, requestContext),
verify: (actual =>
{
- Assert.False(actual.Success);
- Assert.Null(actual.SessionId);
- Assert.Null(actual.RootNode);
+ Assert.NotNull(actual.SessionId);
+ Assert.NotNull(actual);
+ Assert.True(actual.ErrorMessage.Contains(expectedExceptionText));
}));
// And expect error notification to be sent
- serviceHostMock.Verify(x => x.SendEvent(ConnectionCompleteNotification.Type, It.IsAny()), Times.Once());
- Assert.NotNull(completeParams);
- Assert.True(completeParams.Messages.Contains(expectedExceptionText));
+ serviceHostMock.Verify(x => x.SendEvent(CreateSessionCompleteNotification.Type, It.IsAny()), Times.Once());
}
-
+
[Fact]
public async Task CreateSessionRequestWithMasterConnectionReturnsServerSuccessAndNodeInfo()
@@ -138,10 +134,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
NodePath = "Any path"
};
+
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleExpandRequest(expandParams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallServiceExpand(expandParams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, expandParams.SessionId);
@@ -160,8 +157,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleRefreshRequest(expandParams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallServiceRefresh(expandParams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, expandParams.SessionId);
@@ -179,8 +176,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleCloseSessionRequest(closeSessionParamsparams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallCloseSession(closeSessionParamsparams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, closeSessionParamsparams.SessionId);
@@ -199,8 +196,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleCloseSessionRequest(closeSessionParamsparams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallCloseSession(closeSessionParamsparams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, closeSessionParamsparams.SessionId);
@@ -211,7 +208,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
connectionServiceMock.Verify(c => c.Disconnect(It.IsAny()));
}
- private async Task CreateSession()
+ private async Task CreateSession()
{
ConnectionDetails details = new ConnectionDetails()
{
@@ -221,9 +218,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
ServerName = "serverName"
};
- serviceHostMock.AddEventHandling(ConnectionCompleteNotification.Type, null);
+ SessionCreatedParameters sessionResult = null;
+ serviceHostMock.AddEventHandling(CreateSessionCompleteNotification.Type, (et, p) => sessionResult = p);
CreateSessionResponse result = default(CreateSessionResponse);
var contextMock = RequestContextMocks.Create(r => result = r).AddErrorHandling(null);
+
connectionServiceMock.Setup(c => c.Connect(It.IsAny()))
.Returns((ConnectParams connectParams) => Task.FromResult(GetCompleteParamsForConnection(connectParams.OwnerUri, details)));
@@ -236,8 +235,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
connectionServiceMock.Setup(c => c.Disconnect(It.IsAny())).Returns(true);
await service.HandleCreateSessionRequest(details, contextMock.Object);
+ await service.CreateSessionTask;
- return result;
+ return sessionResult;
}
private async Task ExpandAndVerifyServerNodes()
@@ -251,8 +251,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleExpandRequest(expandParams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallServiceExpand(expandParams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, session.SessionId);
@@ -272,8 +272,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
// when expanding
// then expect the nodes are server children
- await RunAndVerify(
- test: (requestContext) => service.HandleRefreshRequest(expandParams, requestContext),
+ await RunAndVerify(
+ test: (requestContext) => CallServiceRefresh(expandParams, requestContext),
verify: (actual =>
{
Assert.Equal(actual.SessionId, session.SessionId);
@@ -282,17 +282,75 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
}));
}
+ private async Task CallServiceRefresh(RefreshParams expandParams, RequestContext requestContext)
+ {
+ ExpandResponse result = null;
+ serviceHostMock.AddEventHandling(ExpandCompleteNotification.Type, (et, p) => result = p);
+
+ await service.HandleRefreshRequest(expandParams, requestContext);
+ Task task = service.ExpandTask;
+ if (task != null)
+ {
+ await task;
+ }
+
+ return result;
+
+ }
+
+ private async Task CallServiceExpand(ExpandParams expandParams, RequestContext requestContext)
+ {
+ ExpandResponse result = null;
+ serviceHostMock.AddEventHandling(ExpandCompleteNotification.Type, (et, p) => result = p);
+
+ await service.HandleExpandRequest(expandParams, requestContext);
+ Task task = service.ExpandTask;
+ if (task != null)
+ {
+ await task;
+ }
+ return result;
+ }
+
+ private async Task CallCreateSession(ConnectionDetails connectionDetails, RequestContext context)
+ {
+ SessionCreatedParameters result = null;
+ serviceHostMock.AddEventHandling(CreateSessionCompleteNotification.Type, (et, p) => result = p);
+
+ await service.HandleCreateSessionRequest(connectionDetails, context);
+ Task task = service.CreateSessionTask;
+ if (task != null)
+ {
+ await task;
+ }
+ return result;
+ }
+
+ private async Task CallCloseSession(CloseSessionParams closeSessionParams, RequestContext context)
+ {
+ SessionCreatedParameters result = null;
+ serviceHostMock.AddEventHandling(CreateSessionCompleteNotification.Type, (et, p) => result = p);
+
+ await service.HandleCloseSessionRequest(closeSessionParams, context);
+ return null;
+ }
+
private async Task CreateSessionRequestAndVerifyServerNodeHelper(ConnectionDetails details)
{
serviceHostMock.AddEventHandling(ConnectionCompleteNotification.Type, null);
+ //SessionCreatedParameters sessionResult
+
connectionServiceMock.Setup(c => c.Connect(It.IsAny()))
.Returns((ConnectParams connectParams) => Task.FromResult(GetCompleteParamsForConnection(connectParams.OwnerUri, details)));
// when creating a new session
// then expect the create session request to return false
- await RunAndVerify(
- test: (requestContext) => service.HandleCreateSessionRequest(details, requestContext),
+ await RunAndVerify(
+ test: (requestContext) =>
+ {
+ return CallCreateSession(details, requestContext);
+ },
verify: (actual =>
{
Assert.True(actual.Success);
@@ -339,15 +397,19 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
};
}
- private async Task RunAndVerify(Func, Task> test, Action verify)
+ private async Task RunAndVerify(Func, Task> test, Action verify)
{
T result = default(T);
var contextMock = RequestContextMocks.Create(r => result = r).AddErrorHandling(null);
- await test(contextMock.Object);
- VerifyResult(contextMock, verify, result);
+ TResult actualResult = await test(contextMock.Object);
+ if (actualResult == null && typeof(TResult) == typeof(T))
+ {
+ actualResult = (TResult)Convert.ChangeType(result, typeof(TResult));
+ }
+ VerifyResult(contextMock, verify, actualResult);
}
- private void VerifyResult(Mock> contextMock, Action verify, T actual)
+ private void VerifyResult(Mock> contextMock, Action verify, TResult actual)
{
contextMock.Verify(c => c.SendResult(It.IsAny()), Times.Once);
contextMock.Verify(c => c.SendError(It.IsAny(), It.IsAny()), Times.Never);
@@ -359,6 +421,5 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
contextMock.Verify(c => c.SendResult(It.IsAny()), Times.Never);
contextMock.Verify(c => c.SendError(It.IsAny(), It.IsAny()), Times.Once);
}
-
}
}