mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-15 01:25:40 -05:00
Object explorer notifications (#354)
* changing oe protocols to send notifications
This commit is contained in:
@@ -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<CreateSessionResponse> context)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest");
|
||||
|
||||
Func<Task<CreateSessionResponse>> 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<bool> context)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleExpandRequest");
|
||||
|
||||
Func<Task<bool>> 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<bool> 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<CloseSessionResponse> context)
|
||||
{
|
||||
|
||||
Logger.Write(LogLevel.Verbose, "HandleCloseSessionRequest");
|
||||
Func<Task<CloseSessionResponse>> 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<CreateSessionResponse> context)
|
||||
|
||||
private void RunCreateSessionTask(ConnectionDetails connectionDetails, string uri)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest");
|
||||
Func<Task<CreateSessionResponse>> 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);
|
||||
/// <summary>
|
||||
/// For tests only
|
||||
/// </summary>
|
||||
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<SessionCreatedParameters> 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<NodeInfo[]> 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<ConnectionCompleteParams> Connect(ConnectParams connectParams)
|
||||
private async Task<ConnectionCompleteParams> 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<ExpandResponse> context)
|
||||
private void RunExpandTask(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleExpandRequest");
|
||||
Func<Task<ExpandResponse>> 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<RefreshResponse> context)
|
||||
/// <summary>
|
||||
/// For tests only
|
||||
/// </summary>
|
||||
internal Task ExpandTask
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleRefreshRequest");
|
||||
Func<Task<RefreshResponse>> 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<CloseSessionResponse> 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<Task<CloseSessionResponse>> 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<T>(Func<Task<T>> handler, RequestContext<T> requestContext, string requestType)
|
||||
|
||||
|
||||
private async Task<T> HandleRequestAsync<T>(Func<Task<T>> handler, RequestContext<T> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<ConnectionService>());
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user