mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Added AzureMonitor to Microsoft.Kusto.ServiceLayer (#1208)
* Added AzureMonitor to Microsoft.Kusto.ServiceLayer. * Added Intellisense for AzureMonitor. Moved Intellisense logic from KustoIntellisenseClient to IntellisenseClientBase. * Added ServiceName as a command parameter for starting Kusto. * Added check to return null if connectionInfo is not in the connectionService. * Added support for Dashboard in MetadataService and AdminService. * Removed workspace id from databaseName for Monitor. Added logic for MetadataService and AdminService to return different information for AzureMonitor. * Moved providerName and providerDescription to DataSourceFactory. * Changed DatabaseName to include Name and Id. Changed ProviderName to LOGANALYTICS in DataSourceFactory * Fixed unit tests * Changed logic to use ServiceName instead of server to determine DataSourceType * Code review feedback and reverted changes to ObjectExplorerService. * Removed unused reference from HostLoader * Changed Parallel.Invoke to await Task.Run * Moved Kusto datasource and supporting classes to separate directory. * Removed unused datasourceFactory from ConnectionService. Added GetDatabases and GetDatabaseInfo to IDataSource and child classes * Renamed Instance variables in ObjectExplorerService. Removed unused attribute on TSqlFormatterService. Removed invalid comment in ConnectionService. * Fixed warnings in build. * Moved SizeInMB to DatabaseMetadata. Refactored ConvertToDatabaseInfo * Fixed unit test
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
<PackageReference Update="Microsoft.Azure.Kusto.Language" Version="9.0.4"/>
|
<PackageReference Update="Microsoft.Azure.Kusto.Language" Version="9.0.4"/>
|
||||||
<PackageReference Update="Microsoft.SqlServer.Assessment" Version="[1.0.305]" />
|
<PackageReference Update="Microsoft.SqlServer.Assessment" Version="[1.0.305]" />
|
||||||
<PackageReference Update="Microsoft.SqlServer.Migration.Assessment" Version="1.0.20210614.603" />
|
<PackageReference Update="Microsoft.SqlServer.Migration.Assessment" Version="1.0.20210614.603" />
|
||||||
|
<PackageReference Update="Microsoft.Azure.OperationalInsights" Version="1.0.0" />
|
||||||
|
|
||||||
<PackageReference Update="Moq" Version="4.8.2" />
|
<PackageReference Update="Moq" Version="4.8.2" />
|
||||||
<PackageReference Update="nunit" Version="3.12.0" />
|
<PackageReference Update="nunit" Version="3.12.0" />
|
||||||
|
|||||||
@@ -42,33 +42,22 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle get database info request
|
/// Handle get database info request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task HandleGetDatabaseInfoRequest(
|
private async Task HandleGetDatabaseInfoRequest(GetDatabaseInfoParams databaseParams, RequestContext<GetDatabaseInfoResponse> requestContext)
|
||||||
GetDatabaseInfoParams databaseParams,
|
|
||||||
RequestContext<GetDatabaseInfoResponse> requestContext)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Func<Task> requestHandler = async () =>
|
var infoResponse = await Task.Run(() =>
|
||||||
{
|
{
|
||||||
_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo);
|
|
||||||
DatabaseInfo info = null;
|
DatabaseInfo info = null;
|
||||||
|
if (_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo))
|
||||||
if (connInfo != null)
|
|
||||||
{
|
{
|
||||||
info = GetDatabaseInfo(connInfo);
|
info = GetDatabaseInfo(connInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
await requestContext.SendResult(new GetDatabaseInfoResponse()
|
return new GetDatabaseInfoResponse {DatabaseInfo = info};
|
||||||
{
|
|
||||||
DatabaseInfo = info
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
|
|
||||||
{
|
|
||||||
await requestContext.SendError(t.Exception.ToString());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await requestContext.SendResult(infoResponse);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -88,18 +77,9 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReliableDataSourceConnection connection;
|
connInfo.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
||||||
connInfo.TryGetConnection("Default", out connection);
|
|
||||||
IDataSource dataSource = connection.GetUnderlyingConnection();
|
IDataSource dataSource = connection.GetUnderlyingConnection();
|
||||||
DataSourceObjectMetadata objectMetadata =
|
return dataSource.GetDatabaseInfo(connInfo.ConnectionDetails.ServerName, connInfo.ConnectionDetails.DatabaseName);
|
||||||
MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
|
|
||||||
|
|
||||||
List<DataSourceObjectMetadata> metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
|
|
||||||
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
|
|
||||||
|
|
||||||
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
|
|
||||||
|
|
||||||
return databaseInfo.ElementAtOrDefault(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
using Microsoft.SqlServer.Management.Common;
|
||||||
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||||
@@ -56,8 +53,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
|
|
||||||
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
|
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
|
||||||
|
|
||||||
private IDataSourceFactory _dataSourceFactory;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map from script URIs to ConnectionInfo objects
|
/// Map from script URIs to ConnectionInfo objects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -84,55 +79,9 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Service host object for sending/receiving requests/events.
|
/// Service host object for sending/receiving requests/events.
|
||||||
/// Internal for testing purposes.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IProtocolEndpoint ServiceHost { get; set; }
|
private IProtocolEndpoint _serviceHost;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the connection queue
|
|
||||||
/// </summary>
|
|
||||||
internal IConnectedBindingQueue ConnectionQueue
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.GetConnectedQueue("Default");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default constructor should be private since it's a singleton class, but we need a constructor
|
|
||||||
/// for use in unit test mocking.
|
|
||||||
/// </summary>
|
|
||||||
public ConnectionService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a connection queue for given type
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public IConnectedBindingQueue GetConnectedQueue(string type)
|
|
||||||
{
|
|
||||||
IConnectedBindingQueue connectedBindingQueue;
|
|
||||||
if (connectedQueues.TryGetValue(type, out connectedBindingQueue))
|
|
||||||
{
|
|
||||||
return connectedBindingQueue;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all the connection queues
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<IConnectedBindingQueue> ConnectedQueues
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.connectedQueues.Values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a new connection queue if not already registered
|
/// Register a new connection queue if not already registered
|
||||||
@@ -182,9 +131,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <param name="connectionParams">The params to validate</param>
|
/// <param name="connectionParams">The params to validate</param>
|
||||||
/// <returns>A ConnectionCompleteParams object upon validation error,
|
/// <returns>A ConnectionCompleteParams object upon validation error,
|
||||||
/// null upon validation success</returns>
|
/// null upon validation success</returns>
|
||||||
public ConnectionCompleteParams ValidateConnectParams(ConnectParams connectionParams)
|
private ConnectionCompleteParams ValidateConnectParams(ConnectParams connectionParams)
|
||||||
{
|
{
|
||||||
string paramValidationErrorMessage;
|
|
||||||
if (connectionParams == null)
|
if (connectionParams == null)
|
||||||
{
|
{
|
||||||
return new ConnectionCompleteParams
|
return new ConnectionCompleteParams
|
||||||
@@ -192,7 +140,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
Messages = SR.ConnectionServiceConnectErrorNullParams
|
Messages = SR.ConnectionServiceConnectErrorNullParams
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!connectionParams.IsValid(out paramValidationErrorMessage))
|
if (!connectionParams.IsValid(out string paramValidationErrorMessage))
|
||||||
{
|
{
|
||||||
return new ConnectionCompleteParams
|
return new ConnectionCompleteParams
|
||||||
{
|
{
|
||||||
@@ -208,7 +156,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open a connection with the specified ConnectParams
|
/// Open a connection with the specified ConnectParams
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual async Task<ConnectionCompleteParams> Connect(ConnectParams connectionParams)
|
public async Task<ConnectionCompleteParams> Connect(ConnectParams connectionParams)
|
||||||
{
|
{
|
||||||
// Validate parameters
|
// Validate parameters
|
||||||
ConnectionCompleteParams validationResults = ValidateConnectParams(connectionParams);
|
ConnectionCompleteParams validationResults = ValidateConnectParams(connectionParams);
|
||||||
@@ -222,9 +170,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
connectionParams.Connection.ApplicationName = GetApplicationNameWithFeature(connectionParams.Connection.ApplicationName, connectionParams.Purpose);
|
connectionParams.Connection.ApplicationName = GetApplicationNameWithFeature(connectionParams.Connection.ApplicationName, connectionParams.Purpose);
|
||||||
// If there is no ConnectionInfo in the map, create a new ConnectionInfo,
|
// If there is no ConnectionInfo in the map, create a new ConnectionInfo,
|
||||||
// but wait until later when we are connected to add it to the map.
|
// but wait until later when we are connected to add it to the map.
|
||||||
ConnectionInfo connectionInfo;
|
|
||||||
bool connectionChanged = false;
|
bool connectionChanged = false;
|
||||||
if (!OwnerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo))
|
if (!OwnerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out ConnectionInfo connectionInfo))
|
||||||
{
|
{
|
||||||
connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||||
}
|
}
|
||||||
@@ -278,7 +225,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
Resource = "SQL"
|
Resource = "SQL"
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = Instance.ServiceHost.SendRequest(SecurityTokenRequest.Type, requestMessage, true).Result;
|
var response = _serviceHost.SendRequest(SecurityTokenRequest.Type, requestMessage, true).Result;
|
||||||
connection.UpdateAuthToken(response.Token);
|
connection.UpdateAuthToken(response.Token);
|
||||||
|
|
||||||
return response.Token;
|
return response.Token;
|
||||||
@@ -759,56 +706,31 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// List all databases on the server specified
|
/// List all databases on the server specified
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ListDatabasesResponse ListDatabases(ListDatabasesParams listDatabasesParams)
|
private ListDatabasesResponse ListDatabases(ListDatabasesParams listDatabasesParams)
|
||||||
{
|
{
|
||||||
// Verify parameters
|
// Verify parameters
|
||||||
var owner = listDatabasesParams.OwnerUri;
|
if (string.IsNullOrEmpty(listDatabasesParams.OwnerUri))
|
||||||
if (string.IsNullOrEmpty(owner))
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException(SR.ConnectionServiceListDbErrorNullOwnerUri);
|
throw new ArgumentException(SR.ConnectionServiceListDbErrorNullOwnerUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the existing connection as a base for the search
|
// Use the existing connection as a base for the search
|
||||||
ConnectionInfo info;
|
if (!TryFindConnection(listDatabasesParams.OwnerUri, out ConnectionInfo info))
|
||||||
if (!TryFindConnection(owner, out info))
|
|
||||||
{
|
{
|
||||||
throw new Exception(SR.ConnectionServiceListDbErrorNotConnected(owner));
|
throw new Exception(SR.ConnectionServiceListDbErrorNotConnected(listDatabasesParams.OwnerUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
info.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
info.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
||||||
IDataSource dataSource = connection.GetUnderlyingConnection();
|
IDataSource dataSource = connection.GetUnderlyingConnection();
|
||||||
|
|
||||||
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
|
return dataSource.GetDatabases(info.ConnectionDetails.ServerName, listDatabasesParams.IncludeDetails.HasTrue());
|
||||||
|
|
||||||
ListDatabasesResponse response = new ListDatabasesResponse();
|
|
||||||
|
|
||||||
// Mainly used by "manage" dashboard
|
|
||||||
if (listDatabasesParams.IncludeDetails.HasTrue())
|
|
||||||
{
|
|
||||||
IEnumerable<DataSourceObjectMetadata> databaseMetadataInfo = dataSource.GetChildObjects(objectMetadata, true);
|
|
||||||
List<DatabaseInfo> metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
|
|
||||||
response.Databases = metadata.ToArray();
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<DataSourceObjectMetadata> databaseMetadata = dataSource.GetChildObjects(objectMetadata);
|
|
||||||
if (databaseMetadata != null)
|
|
||||||
{
|
|
||||||
response.DatabaseNames = databaseMetadata
|
|
||||||
.Select(objMeta => objMeta.PrettyName == objMeta.Name ? objMeta.PrettyName : $"{objMeta.PrettyName} ({objMeta.Name})")
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
|
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
|
||||||
IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
|
IConnectedBindingQueue connectedBindingQueue)
|
||||||
{
|
{
|
||||||
ServiceHost = serviceHost;
|
_serviceHost = serviceHost;
|
||||||
_dataSourceConnectionFactory = dataSourceConnectionFactory;
|
_dataSourceConnectionFactory = dataSourceConnectionFactory;
|
||||||
_dataSourceFactory = dataSourceFactory;
|
|
||||||
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
|
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
|
||||||
LockedDatabaseManager.ConnectionService = this;
|
LockedDatabaseManager.ConnectionService = this;
|
||||||
|
|
||||||
@@ -845,15 +767,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <param name="connectParams"></param>
|
/// <param name="connectParams"></param>
|
||||||
/// <param name="requestContext"></param>
|
/// <param name="requestContext"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task HandleConnectRequest(
|
private async Task HandleConnectRequest(ConnectParams connectParams, RequestContext<bool> requestContext)
|
||||||
ConnectParams connectParams,
|
|
||||||
RequestContext<bool> requestContext)
|
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, "HandleConnectRequest");
|
Logger.Write(TraceEventType.Verbose, "HandleConnectRequest");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RunConnectRequestHandlerTask(connectParams);
|
await Task.Run(async () => await RunConnectRequestHandlerTask(connectParams));
|
||||||
await requestContext.SendResult(true);
|
await requestContext.SendResult(true);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -862,34 +782,22 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunConnectRequestHandlerTask(ConnectParams connectParams)
|
private async Task RunConnectRequestHandlerTask(ConnectParams connectParams)
|
||||||
{
|
{
|
||||||
// create a task to connect asynchronously so that other requests are not blocked in the meantime
|
try
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try
|
// open connection based on request details
|
||||||
|
ConnectionCompleteParams result = await Connect(connectParams);
|
||||||
|
await _serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var result = new ConnectionCompleteParams
|
||||||
{
|
{
|
||||||
// result is null if the ConnectParams was successfully validated
|
Messages = ex.ToString()
|
||||||
ConnectionCompleteParams result = ValidateConnectParams(connectParams);
|
};
|
||||||
if (result != null)
|
await _serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||||
{
|
}
|
||||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open connection based on request details
|
|
||||||
result = await Connect(connectParams);
|
|
||||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConnectionCompleteParams result = new ConnectionCompleteParams()
|
|
||||||
{
|
|
||||||
Messages = ex.ToString()
|
|
||||||
};
|
|
||||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
|
||||||
}
|
|
||||||
}).ContinueWithOnFaulted(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -915,15 +823,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle disconnect requests
|
/// Handle disconnect requests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task HandleDisconnectRequest(
|
private async Task HandleDisconnectRequest(DisconnectParams disconnectParams, RequestContext<bool> requestContext)
|
||||||
DisconnectParams disconnectParams,
|
|
||||||
RequestContext<bool> requestContext)
|
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, "HandleDisconnectRequest");
|
Logger.Write(TraceEventType.Verbose, "HandleDisconnectRequest");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool result = Instance.Disconnect(disconnectParams);
|
bool result = Disconnect(disconnectParams);
|
||||||
await requestContext.SendResult(result);
|
await requestContext.SendResult(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -936,15 +842,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle requests to list databases on the current server
|
/// Handle requests to list databases on the current server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task HandleListDatabasesRequest(
|
private async Task HandleListDatabasesRequest(ListDatabasesParams listDatabasesParams, RequestContext<ListDatabasesResponse> requestContext)
|
||||||
ListDatabasesParams listDatabasesParams,
|
|
||||||
RequestContext<ListDatabasesResponse> requestContext)
|
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, "ListDatabasesRequest");
|
Logger.Write(TraceEventType.Verbose, "ListDatabasesRequest");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ListDatabasesResponse result = ListDatabases(listDatabasesParams);
|
var result = await Task.Run(() => ListDatabases(listDatabasesParams));
|
||||||
await requestContext.SendResult(result);
|
await requestContext.SendResult(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1025,11 +929,10 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles a request to change the database for a connection
|
/// Handles a request to change the database for a connection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task HandleChangeDatabaseRequest(
|
private async Task HandleChangeDatabaseRequest(ChangeDatabaseParams changeDatabaseParams, RequestContext<bool> requestContext)
|
||||||
ChangeDatabaseParams changeDatabaseParams,
|
|
||||||
RequestContext<bool> requestContext)
|
|
||||||
{
|
{
|
||||||
await requestContext.SendResult(ChangeConnectionDatabaseContext(changeDatabaseParams.OwnerUri, changeDatabaseParams.NewDatabase, true));
|
bool result = await Task.Run(() => result = ChangeConnectionDatabaseContext(changeDatabaseParams.OwnerUri, changeDatabaseParams.NewDatabase, true));
|
||||||
|
await requestContext.SendResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1037,60 +940,57 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ownerUri">URI of the owner of the connection</param>
|
/// <param name="ownerUri">URI of the owner of the connection</param>
|
||||||
/// <param name="newDatabaseName">Name of the database to change the connection to</param>
|
/// <param name="newDatabaseName">Name of the database to change the connection to</param>
|
||||||
public bool ChangeConnectionDatabaseContext(string ownerUri, string newDatabaseName, bool force = false)
|
private bool ChangeConnectionDatabaseContext(string ownerUri, string newDatabaseName, bool force = false)
|
||||||
{
|
{
|
||||||
ConnectionInfo info;
|
if (!TryFindConnection(ownerUri, out ConnectionInfo info))
|
||||||
if (TryFindConnection(ownerUri, out info))
|
|
||||||
{
|
{
|
||||||
try
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
info.ConnectionDetails.DatabaseName = newDatabaseName;
|
||||||
|
|
||||||
|
foreach (string key in info.AllConnectionTypes)
|
||||||
{
|
{
|
||||||
info.ConnectionDetails.DatabaseName = newDatabaseName;
|
ReliableDataSourceConnection conn;
|
||||||
|
info.TryGetConnection(key, out conn);
|
||||||
foreach (string key in info.AllConnectionTypes)
|
if (conn != null && conn.Database != newDatabaseName)
|
||||||
{
|
{
|
||||||
ReliableDataSourceConnection conn;
|
if (info.IsCloud && force)
|
||||||
info.TryGetConnection(key, out conn);
|
|
||||||
if (conn != null && conn.Database != newDatabaseName)
|
|
||||||
{
|
{
|
||||||
if (info.IsCloud && force)
|
conn.Close();
|
||||||
{
|
conn.Dispose();
|
||||||
conn.Close();
|
info.RemoveConnection(key);
|
||||||
conn.Dispose();
|
|
||||||
info.RemoveConnection(key);
|
|
||||||
|
|
||||||
// create a kusto connection instance
|
// create a kusto connection instance
|
||||||
ReliableDataSourceConnection connection = info.Factory.CreateDataSourceConnection(info.ConnectionDetails, ownerUri);
|
ReliableDataSourceConnection connection = info.Factory.CreateDataSourceConnection(info.ConnectionDetails, ownerUri);
|
||||||
connection.Open();
|
connection.Open();
|
||||||
info.AddConnection(key, connection);
|
info.AddConnection(key, connection);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
conn.ChangeDatabase(newDatabaseName);
|
conn.ChangeDatabase(newDatabaseName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fire a connection changed event
|
// Fire a connection changed event
|
||||||
ConnectionChangedParams parameters = new ConnectionChangedParams();
|
ConnectionChangedParams parameters = new ConnectionChangedParams();
|
||||||
IConnectionSummary summary = info.ConnectionDetails;
|
IConnectionSummary summary = info.ConnectionDetails;
|
||||||
parameters.Connection = summary.Clone();
|
parameters.Connection = summary.Clone();
|
||||||
parameters.OwnerUri = ownerUri;
|
parameters.OwnerUri = ownerUri;
|
||||||
ServiceHost.SendEvent(ConnectionChangedNotification.Type, parameters);
|
_serviceHost.SendEvent(ConnectionChangedNotification.Type, parameters);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Write(
|
Logger.Write(
|
||||||
TraceEventType.Error,
|
TraceEventType.Error,
|
||||||
string.Format(
|
$"Exception caught while trying to change database context to [{newDatabaseName}] for OwnerUri [{ownerUri}]. Exception:{e}"
|
||||||
"Exception caught while trying to change database context to [{0}] for OwnerUri [{1}]. Exception:{2}",
|
);
|
||||||
newDatabaseName,
|
return false;
|
||||||
ownerUri,
|
|
||||||
e.ToString())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1129,12 +1029,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
/// <param name="info"></param>
|
/// <param name="info"></param>
|
||||||
private void HandleDisconnectTelemetry(ConnectionInfo connectionInfo)
|
private void HandleDisconnectTelemetry(ConnectionInfo connectionInfo)
|
||||||
{
|
{
|
||||||
if (ServiceHost != null)
|
if (_serviceHost != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send a telemetry notification for intellisense performance metrics
|
// Send a telemetry notification for intellisense performance metrics
|
||||||
ServiceHost.SendEvent(TelemetryNotification.Type, new TelemetryParams()
|
_serviceHost.SendEvent(TelemetryNotification.Type, new TelemetryParams()
|
||||||
{
|
{
|
||||||
Params = new TelemetryProperties
|
Params = new TelemetryProperties
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
@@ -100,13 +102,16 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
public abstract CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition,
|
public abstract CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition,
|
||||||
bool throwOnError = false);
|
bool throwOnError = false);
|
||||||
|
|
||||||
|
public abstract ListDatabasesResponse GetDatabases(string serverName, bool includeDetails);
|
||||||
|
public abstract DatabaseInfo GetDatabaseInfo(string serverName, string databaseName);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DataSourceType DataSourceType { get; protected set; }
|
public DataSourceType DataSourceType { get; protected set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public abstract string ClusterName { get; }
|
public abstract string ClusterName { get; }
|
||||||
|
|
||||||
public abstract string DatabaseName { get; }
|
public abstract string DatabaseName { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
|||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
@@ -16,8 +18,14 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
[Export(typeof(IDataSourceFactory))]
|
[Export(typeof(IDataSourceFactory))]
|
||||||
public class DataSourceFactory : IDataSourceFactory
|
public class DataSourceFactory : IDataSourceFactory
|
||||||
{
|
{
|
||||||
public IDataSource Create(DataSourceType dataSourceType, ConnectionDetails connectionDetails, string ownerUri)
|
private const string KustoProviderName = "KUSTO";
|
||||||
|
private const string LogAnalyticsProviderName = "LOGANALYTICS";
|
||||||
|
private const string KustoProviderDescription = "Microsoft Azure Data Explorer";
|
||||||
|
private const string LogAnalyticsProviderDescription = "Microsoft Azure Monitor Explorer";
|
||||||
|
|
||||||
|
public IDataSource Create(ConnectionDetails connectionDetails, string ownerUri)
|
||||||
{
|
{
|
||||||
|
var dataSourceType = GetDataSourceType();
|
||||||
switch (dataSourceType)
|
switch (dataSourceType)
|
||||||
{
|
{
|
||||||
case DataSourceType.Kusto:
|
case DataSourceType.Kusto:
|
||||||
@@ -27,7 +35,12 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
var intellisenseClient = new KustoIntellisenseClient(kustoClient);
|
var intellisenseClient = new KustoIntellisenseClient(kustoClient);
|
||||||
return new KustoDataSource(kustoClient, intellisenseClient);
|
return new KustoDataSource(kustoClient, intellisenseClient);
|
||||||
}
|
}
|
||||||
|
case DataSourceType.LogAnalytics:
|
||||||
|
{
|
||||||
|
var httpClient = new MonitorClient(connectionDetails.ServerName, connectionDetails.AccountToken);
|
||||||
|
var intellisenseClient = new MonitorIntellisenseClient(httpClient);
|
||||||
|
return new MonitorDataSource(httpClient, intellisenseClient);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
|
|
||||||
throw new ArgumentException($@"Unsupported data source type ""{dataSourceType}""",
|
throw new ArgumentException($@"Unsupported data source type ""{dataSourceType}""",
|
||||||
@@ -35,6 +48,11 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DataSourceType GetDataSourceType()
|
||||||
|
{
|
||||||
|
return Program.ServiceName.Contains("Kusto") ? DataSourceType.Kusto : DataSourceType.LogAnalytics;
|
||||||
|
}
|
||||||
|
|
||||||
private DataSourceConnectionDetails MapKustoConnectionDetails(ConnectionDetails connectionDetails)
|
private DataSourceConnectionDetails MapKustoConnectionDetails(ConnectionDetails connectionDetails)
|
||||||
{
|
{
|
||||||
if (connectionDetails.AuthenticationType == "dstsAuth" || connectionDetails.AuthenticationType == "AzureMFA")
|
if (connectionDetails.AuthenticationType == "dstsAuth" || connectionDetails.AuthenticationType == "AzureMFA")
|
||||||
@@ -95,7 +113,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unsupported data source type \"{dataSourceType}\"", nameof(dataSourceType));
|
throw new ArgumentException($@"Unsupported data source type ""{dataSourceType}""", nameof(dataSourceType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +127,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unsupported data source type \"{dataSourceType}\"", nameof(dataSourceType));
|
throw new ArgumentException($@"Unsupported data source type ""{dataSourceType}""", nameof(dataSourceType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,8 +144,18 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unsupported data source type \"{dataSourceType}\"", nameof(dataSourceType));
|
throw new ArgumentException($@"Unsupported data source type ""{dataSourceType}""", nameof(dataSourceType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetProviderName()
|
||||||
|
{
|
||||||
|
return Program.ServiceName.Contains("Kusto") ? KustoProviderName : LogAnalyticsProviderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetProviderDescription()
|
||||||
|
{
|
||||||
|
return Program.ServiceName.Contains("Kusto") ? KustoProviderDescription : LogAnalyticsProviderDescription;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,6 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An Operations Management Suite (OMS) Log Analytics workspace.
|
/// An Operations Management Suite (OMS) Log Analytics workspace.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
OmsLogAnalytics
|
LogAnalytics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
@@ -117,5 +119,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn, bool throwOnError = false);
|
DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn, bool throwOnError = false);
|
||||||
Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
||||||
CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
||||||
|
ListDatabasesResponse GetDatabases(string serverName, bool includeDetails);
|
||||||
|
DatabaseInfo GetDatabaseInfo(string serverName, string databaseName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,6 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
{
|
{
|
||||||
public interface IDataSourceFactory
|
public interface IDataSourceFactory
|
||||||
{
|
{
|
||||||
IDataSource Create(DataSourceType dataSourceType, ConnectionDetails connectionDetails, string ownerUri);
|
IDataSource Create(ConnectionDetails connectionDetails, string ownerUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
|
||||||
{
|
|
||||||
public interface IIntellisenseClient
|
|
||||||
{
|
|
||||||
void UpdateDatabase(string databaseName);
|
|
||||||
ScriptFileMarker[] GetSemanticMarkers(ScriptParseInfo parseInfo, ScriptFile scriptFile, string queryText);
|
|
||||||
DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn, bool throwOnError = false);
|
|
||||||
Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
|
||||||
CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Kusto.Language;
|
||||||
|
using Kusto.Language.Editor;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
|
using CompletionItem = Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts.CompletionItem;
|
||||||
|
using Diagnostic = Kusto.Language.Diagnostic;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
||||||
|
{
|
||||||
|
public abstract class IntellisenseClientBase
|
||||||
|
{
|
||||||
|
protected GlobalState schemaState;
|
||||||
|
|
||||||
|
public abstract void UpdateDatabase(string databaseName);
|
||||||
|
|
||||||
|
public ScriptFileMarker[] GetSemanticMarkers(ScriptParseInfo parseInfo, ScriptFile scriptFile, string queryText)
|
||||||
|
{
|
||||||
|
var kustoCodeService = new KustoCodeService(queryText, schemaState);
|
||||||
|
var script = CodeScript.From(queryText, schemaState);
|
||||||
|
var parseResult = new List<Diagnostic>();
|
||||||
|
|
||||||
|
foreach (var codeBlock in script.Blocks)
|
||||||
|
{
|
||||||
|
parseResult.AddRange(codeBlock.Service.GetDiagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
parseInfo.ParseResult = parseResult;
|
||||||
|
|
||||||
|
if (!parseResult.Any())
|
||||||
|
{
|
||||||
|
return Array.Empty<ScriptFileMarker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a list of Kusto script file markers from the errors.
|
||||||
|
var markers = new List<ScriptFileMarker>();
|
||||||
|
|
||||||
|
foreach (var error in parseResult)
|
||||||
|
{
|
||||||
|
script.TryGetLineAndOffset(error.Start, out var startLine, out var startOffset);
|
||||||
|
script.TryGetLineAndOffset(error.End, out var endLine, out var endOffset);
|
||||||
|
|
||||||
|
// vscode specific format for error markers.
|
||||||
|
markers.Add(new ScriptFileMarker
|
||||||
|
{
|
||||||
|
Message = error.Message,
|
||||||
|
Level = ScriptFileMarkerLevel.Error,
|
||||||
|
ScriptRegion = new ScriptRegion
|
||||||
|
{
|
||||||
|
File = scriptFile.FilePath,
|
||||||
|
StartLineNumber = startLine,
|
||||||
|
StartColumnNumber = startOffset,
|
||||||
|
StartOffset = 0,
|
||||||
|
EndLineNumber = endLine,
|
||||||
|
EndColumnNumber = endOffset,
|
||||||
|
EndOffset = 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn,
|
||||||
|
bool throwOnError = false)
|
||||||
|
{
|
||||||
|
//TODOKusto: API wasnt working properly, need to check that part.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false)
|
||||||
|
{
|
||||||
|
var script = CodeScript.From(scriptDocumentInfo.Contents, schemaState);
|
||||||
|
script.TryGetTextPosition(textPosition.Line + 1, textPosition.Character + 1, out int position);
|
||||||
|
|
||||||
|
var codeBlock = script.GetBlockAtPosition(position);
|
||||||
|
var quickInfo = codeBlock.Service.GetQuickInfo(position);
|
||||||
|
|
||||||
|
return AutoCompleteHelper.ConvertQuickInfoToHover(
|
||||||
|
quickInfo.Text,
|
||||||
|
"kusto",
|
||||||
|
scriptDocumentInfo.StartLine,
|
||||||
|
scriptDocumentInfo.StartColumn,
|
||||||
|
textPosition.Character);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition,
|
||||||
|
bool throwOnError = false)
|
||||||
|
{
|
||||||
|
var script = CodeScript.From(scriptDocumentInfo.Contents, schemaState);
|
||||||
|
script.TryGetTextPosition(textPosition.Line + 1, textPosition.Character + 1, out int position); // Gets the actual offset based on line and local offset
|
||||||
|
|
||||||
|
var codeBlock = script.GetBlockAtPosition(position);
|
||||||
|
var completion = codeBlock.Service.GetCompletionItems(position);
|
||||||
|
scriptDocumentInfo.ScriptParseInfo.CurrentSuggestions = completion.Items; // this is declaration item so removed for now, but keep the info when api gets updated
|
||||||
|
|
||||||
|
var completions = new List<CompletionItem>();
|
||||||
|
foreach (var autoCompleteItem in completion.Items)
|
||||||
|
{
|
||||||
|
var label = autoCompleteItem.DisplayText;
|
||||||
|
var insertText = autoCompleteItem.Kind == CompletionKind.Table || autoCompleteItem.Kind == CompletionKind.Database
|
||||||
|
? KustoQueryUtils.EscapeName(label)
|
||||||
|
: label;
|
||||||
|
|
||||||
|
var completionKind = CreateCompletionItemKind(autoCompleteItem.Kind);
|
||||||
|
completions.Add(AutoCompleteHelper.CreateCompletionItem(label, autoCompleteItem.Kind.ToString(),
|
||||||
|
insertText, completionKind, scriptDocumentInfo.StartLine, scriptDocumentInfo.StartColumn,
|
||||||
|
textPosition.Character));
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletionItemKind CreateCompletionItemKind(CompletionKind kustoKind)
|
||||||
|
{
|
||||||
|
switch (kustoKind)
|
||||||
|
{
|
||||||
|
case CompletionKind.Syntax:
|
||||||
|
return CompletionItemKind.Module;
|
||||||
|
case CompletionKind.Column:
|
||||||
|
return CompletionItemKind.Field;
|
||||||
|
case CompletionKind.Variable:
|
||||||
|
return CompletionItemKind.Variable;
|
||||||
|
case CompletionKind.Table:
|
||||||
|
return CompletionItemKind.File;
|
||||||
|
case CompletionKind.Database:
|
||||||
|
return CompletionItemKind.Method;
|
||||||
|
case CompletionKind.LocalFunction:
|
||||||
|
case CompletionKind.DatabaseFunction:
|
||||||
|
case CompletionKind.BuiltInFunction:
|
||||||
|
case CompletionKind.AggregateFunction:
|
||||||
|
return CompletionItemKind.Function;
|
||||||
|
default:
|
||||||
|
return CompletionItemKind.Keyword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using System.Data;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
public interface IKustoClient
|
public interface IKustoClient
|
||||||
{
|
{
|
||||||
@@ -17,7 +17,7 @@ using Microsoft.Kusto.ServiceLayer.Connection;
|
|||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
public class KustoClient : IKustoClient
|
public class KustoClient : IKustoClient
|
||||||
{
|
{
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
// <copyright file="KustoDataSource.cs" company="Microsoft">
|
// <copyright file="KustoDataSource.cs" company="Microsoft">
|
||||||
// Copyright (c) Microsoft. All Rights Reserved.
|
// Copyright (c) Microsoft. All Rights Reserved.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kusto.Cloud.Platform.Data;
|
using Kusto.Cloud.Platform.Data;
|
||||||
using Kusto.Data;
|
using Kusto.Data;
|
||||||
using Kusto.Data.Data;
|
using Kusto.Data.Data;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Models;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Models;
|
||||||
@@ -21,8 +23,9 @@ using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
|||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents Kusto utilities.
|
/// Represents Kusto utilities.
|
||||||
@@ -30,7 +33,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
public class KustoDataSource : DataSourceBase
|
public class KustoDataSource : DataSourceBase
|
||||||
{
|
{
|
||||||
private readonly IKustoClient _kustoClient;
|
private readonly IKustoClient _kustoClient;
|
||||||
private readonly IIntellisenseClient _intellisenseClient;
|
private readonly IntellisenseClientBase _intellisenseClient;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of databases.
|
/// List of databases.
|
||||||
@@ -57,7 +60,11 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private ConcurrentDictionary<string, IEnumerable<FunctionMetadata>> _functionMetadata = new ConcurrentDictionary<string, IEnumerable<FunctionMetadata>>();
|
private ConcurrentDictionary<string, IEnumerable<FunctionMetadata>> _functionMetadata = new ConcurrentDictionary<string, IEnumerable<FunctionMetadata>>();
|
||||||
|
|
||||||
public override string DatabaseName => _kustoClient.DatabaseName;
|
public override string DatabaseName
|
||||||
|
{
|
||||||
|
get => _kustoClient.DatabaseName;
|
||||||
|
set => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public override string ClusterName => _kustoClient.ClusterName;
|
public override string ClusterName => _kustoClient.ClusterName;
|
||||||
|
|
||||||
@@ -79,7 +86,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prevents a default instance of the <see cref="IDataSource"/> class from being created.
|
/// Prevents a default instance of the <see cref="IDataSource"/> class from being created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public KustoDataSource(IKustoClient kustoClient, IIntellisenseClient intellisenseClient)
|
public KustoDataSource(IKustoClient kustoClient, IntellisenseClientBase intellisenseClient)
|
||||||
{
|
{
|
||||||
_kustoClient = kustoClient;
|
_kustoClient = kustoClient;
|
||||||
_intellisenseClient = intellisenseClient;
|
_intellisenseClient = intellisenseClient;
|
||||||
@@ -835,6 +842,44 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
return _intellisenseClient.GetAutoCompleteSuggestions(scriptDocumentInfo, textPosition, throwOnError);
|
return _intellisenseClient.GetAutoCompleteSuggestions(scriptDocumentInfo, textPosition, throwOnError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override ListDatabasesResponse GetDatabases(string serverName, bool includeDetails)
|
||||||
|
{
|
||||||
|
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(serverName);
|
||||||
|
|
||||||
|
// Mainly used by "manage" dashboard
|
||||||
|
if (includeDetails)
|
||||||
|
{
|
||||||
|
IEnumerable<DataSourceObjectMetadata> databaseMetadataInfo = GetChildObjects(objectMetadata, true);
|
||||||
|
List<DatabaseInfo> metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
|
||||||
|
|
||||||
|
return new ListDatabasesResponse
|
||||||
|
{
|
||||||
|
Databases = metadata.ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<DataSourceObjectMetadata> databaseMetadata = GetChildObjects(objectMetadata);
|
||||||
|
if (databaseMetadata != null)
|
||||||
|
{
|
||||||
|
return new ListDatabasesResponse
|
||||||
|
{
|
||||||
|
DatabaseNames = databaseMetadata
|
||||||
|
.Select(objMeta => objMeta.PrettyName == objMeta.Name ? objMeta.PrettyName : $"{objMeta.PrettyName} ({objMeta.Name})")
|
||||||
|
.ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ListDatabasesResponse();;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DatabaseInfo GetDatabaseInfo(string serverName, string databaseName)
|
||||||
|
{
|
||||||
|
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(serverName);
|
||||||
|
var metadata = GetChildObjects(objectMetadata, true).Where(o => o.Name == databaseName).ToList();
|
||||||
|
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(metadata);
|
||||||
|
return databaseInfo.ElementAtOrDefault(0);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,34 +4,25 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kusto.Language;
|
using Kusto.Language;
|
||||||
using Kusto.Language.Editor;
|
|
||||||
using Kusto.Language.Symbols;
|
using Kusto.Language.Symbols;
|
||||||
using Kusto.Language.Syntax;
|
using Kusto.Language.Syntax;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
|
||||||
using Diagnostic = Kusto.Language.Diagnostic;
|
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
public class KustoIntellisenseClient : IIntellisenseClient
|
public class KustoIntellisenseClient : IntellisenseClientBase
|
||||||
{
|
{
|
||||||
private readonly IKustoClient _kustoClient;
|
private readonly IKustoClient _kustoClient;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SchemaState used for getting intellisense info.
|
|
||||||
/// </summary>
|
|
||||||
private GlobalState _schemaState;
|
|
||||||
|
|
||||||
public KustoIntellisenseClient(IKustoClient kustoClient)
|
public KustoIntellisenseClient(IKustoClient kustoClient)
|
||||||
{
|
{
|
||||||
_kustoClient = kustoClient;
|
_kustoClient = kustoClient;
|
||||||
_schemaState = LoadSchemaState(kustoClient.DatabaseName, kustoClient.ClusterName);
|
schemaState = LoadSchemaState(kustoClient.DatabaseName, kustoClient.ClusterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateDatabase(string databaseName)
|
public override void UpdateDatabase(string databaseName)
|
||||||
{
|
{
|
||||||
_schemaState = LoadSchemaState(databaseName, _kustoClient.ClusterName);
|
schemaState = LoadSchemaState(databaseName, _kustoClient.ClusterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlobalState LoadSchemaState(string databaseName, string clusterName)
|
private GlobalState LoadSchemaState(string databaseName, string clusterName)
|
||||||
@@ -237,135 +228,5 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
|||||||
|
|
||||||
return function.Signatures[0].Parameters;
|
return function.Signatures[0].Parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScriptFileMarker[] GetSemanticMarkers(ScriptParseInfo parseInfo, ScriptFile scriptFile, string queryText)
|
|
||||||
{
|
|
||||||
var kustoCodeService = new KustoCodeService(queryText, _schemaState);
|
|
||||||
var script = CodeScript.From(queryText, _schemaState);
|
|
||||||
var parseResult = new List<Diagnostic>();
|
|
||||||
|
|
||||||
foreach (var codeBlock in script.Blocks)
|
|
||||||
{
|
|
||||||
parseResult.AddRange(codeBlock.Service.GetDiagnostics());
|
|
||||||
}
|
|
||||||
|
|
||||||
parseInfo.ParseResult = parseResult;
|
|
||||||
|
|
||||||
if (!parseResult.Any())
|
|
||||||
{
|
|
||||||
return Array.Empty<ScriptFileMarker>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// build a list of Kusto script file markers from the errors.
|
|
||||||
var markers = new List<ScriptFileMarker>();
|
|
||||||
|
|
||||||
foreach (var error in parseResult)
|
|
||||||
{
|
|
||||||
script.TryGetLineAndOffset(error.Start, out var startLine, out var startOffset);
|
|
||||||
script.TryGetLineAndOffset(error.End, out var endLine, out var endOffset);
|
|
||||||
|
|
||||||
// vscode specific format for error markers.
|
|
||||||
markers.Add(new ScriptFileMarker
|
|
||||||
{
|
|
||||||
Message = error.Message,
|
|
||||||
Level = ScriptFileMarkerLevel.Error,
|
|
||||||
ScriptRegion = new ScriptRegion
|
|
||||||
{
|
|
||||||
File = scriptFile.FilePath,
|
|
||||||
StartLineNumber = startLine,
|
|
||||||
StartColumnNumber = startOffset,
|
|
||||||
StartOffset = 0,
|
|
||||||
EndLineNumber = endLine,
|
|
||||||
EndColumnNumber = endOffset,
|
|
||||||
EndOffset = 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return markers.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn, bool throwOnError = false)
|
|
||||||
{
|
|
||||||
//TODOKusto: API wasnt working properly, need to check that part.
|
|
||||||
var abc = KustoCode.ParseAndAnalyze(queryText, _schemaState);
|
|
||||||
var kustoCodeService = new KustoCodeService(abc);
|
|
||||||
//var kustoCodeService = new KustoCodeService(queryText, globals);
|
|
||||||
var relatedInfo = kustoCodeService.GetRelatedElements(index);
|
|
||||||
|
|
||||||
if (relatedInfo != null && relatedInfo.Elements.Count > 1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false)
|
|
||||||
{
|
|
||||||
var script = CodeScript.From(scriptDocumentInfo.Contents, _schemaState);
|
|
||||||
script.TryGetTextPosition(textPosition.Line + 1, textPosition.Character + 1, out int position);
|
|
||||||
|
|
||||||
var codeBlock = script.GetBlockAtPosition(position);
|
|
||||||
var quickInfo = codeBlock.Service.GetQuickInfo(position);
|
|
||||||
|
|
||||||
return AutoCompleteHelper.ConvertQuickInfoToHover(
|
|
||||||
quickInfo.Text,
|
|
||||||
"kusto",
|
|
||||||
scriptDocumentInfo.StartLine,
|
|
||||||
scriptDocumentInfo.StartColumn,
|
|
||||||
textPosition.Character);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LanguageServices.Contracts.CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false)
|
|
||||||
{
|
|
||||||
var script = CodeScript.From(scriptDocumentInfo.Contents, _schemaState);
|
|
||||||
script.TryGetTextPosition(textPosition.Line + 1, textPosition.Character + 1,
|
|
||||||
out int position); // Gets the actual offset based on line and local offset
|
|
||||||
|
|
||||||
var codeBlock = script.GetBlockAtPosition(position);
|
|
||||||
var completion = codeBlock.Service.GetCompletionItems(position);
|
|
||||||
scriptDocumentInfo.ScriptParseInfo.CurrentSuggestions =
|
|
||||||
completion.Items; // this is declaration item so removed for now, but keep the info when api gets updated
|
|
||||||
|
|
||||||
var completions = new List<LanguageServices.Contracts.CompletionItem>();
|
|
||||||
foreach (var autoCompleteItem in completion.Items)
|
|
||||||
{
|
|
||||||
var label = autoCompleteItem.DisplayText;
|
|
||||||
var insertText = autoCompleteItem.Kind == CompletionKind.Table || autoCompleteItem.Kind == CompletionKind.Database
|
|
||||||
? KustoQueryUtils.EscapeName(label)
|
|
||||||
: label;
|
|
||||||
|
|
||||||
var completionKind = CreateCompletionItemKind(autoCompleteItem.Kind);
|
|
||||||
completions.Add(AutoCompleteHelper.CreateCompletionItem(label, autoCompleteItem.Kind.ToString(),
|
|
||||||
insertText, completionKind, scriptDocumentInfo.StartLine, scriptDocumentInfo.StartColumn,
|
|
||||||
textPosition.Character));
|
|
||||||
}
|
|
||||||
|
|
||||||
return completions.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletionItemKind CreateCompletionItemKind(CompletionKind kustoKind)
|
|
||||||
{
|
|
||||||
switch (kustoKind)
|
|
||||||
{
|
|
||||||
case CompletionKind.Syntax:
|
|
||||||
return CompletionItemKind.Module;
|
|
||||||
case CompletionKind.Column:
|
|
||||||
return CompletionItemKind.Field;
|
|
||||||
case CompletionKind.Variable:
|
|
||||||
return CompletionItemKind.Variable;
|
|
||||||
case CompletionKind.Table:
|
|
||||||
return CompletionItemKind.File;
|
|
||||||
case CompletionKind.Database:
|
|
||||||
return CompletionItemKind.Method;
|
|
||||||
case CompletionKind.LocalFunction:
|
|
||||||
case CompletionKind.DatabaseFunction:
|
|
||||||
case CompletionKind.BuiltInFunction:
|
|
||||||
case CompletionKind.AggregateFunction:
|
|
||||||
return CompletionItemKind.Function;
|
|
||||||
default:
|
|
||||||
return CompletionItemKind.Keyword;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,12 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Kusto.Language;
|
using Kusto.Language;
|
||||||
using Kusto.Language.Editor;
|
using Kusto.Language.Editor;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kusto specific class for intellisense helper functions.
|
/// Kusto specific class for intellisense helper functions.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||||
{
|
{
|
||||||
internal class KustoResultsReader : DataReaderWrapper
|
internal class KustoResultsReader : DataReaderWrapper
|
||||||
{
|
{
|
||||||
@@ -92,6 +92,18 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SafeAdd(this Dictionary<string, List<DataSourceObjectMetadata>> dictionary, string key, DataSourceObjectMetadata node)
|
||||||
|
{
|
||||||
|
if (dictionary.ContainsKey(key))
|
||||||
|
{
|
||||||
|
dictionary[key].Add(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dictionary[key] = new List<DataSourceObjectMetadata> {node};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a range to a dictionary of ConcurrentDictionary. Adds range to existing IEnumerable within dictionary
|
/// Add a range to a dictionary of ConcurrentDictionary. Adds range to existing IEnumerable within dictionary
|
||||||
/// at the same key.
|
/// at the same key.
|
||||||
|
|||||||
@@ -15,6 +15,6 @@
|
|||||||
|
|
||||||
public string Urn { get; set; }
|
public string Urn { get; set; }
|
||||||
|
|
||||||
public string SizeInMB { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,5 +6,7 @@
|
|||||||
public class DatabaseMetadata : DataSourceObjectMetadata
|
public class DatabaseMetadata : DataSourceObjectMetadata
|
||||||
{
|
{
|
||||||
public string ClusterName { get; set; }
|
public string ClusterName { get; set; }
|
||||||
|
|
||||||
|
public string SizeInMB { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,22 +59,32 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
|
/// Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clusterDBDetails"></param>
|
/// <param name="clusterDbDetails"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static List<DatabaseInfo> ConvertToDatabaseInfo(IEnumerable<DataSourceObjectMetadata> clusterDBDetails)
|
public static List<DatabaseInfo> ConvertToDatabaseInfo(IEnumerable<DataSourceObjectMetadata> clusterDbDetails)
|
||||||
{
|
{
|
||||||
|
if (clusterDbDetails.FirstOrDefault() is not DatabaseMetadata)
|
||||||
|
{
|
||||||
|
return new List<DatabaseInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
var databaseDetails = new List<DatabaseInfo>();
|
var databaseDetails = new List<DatabaseInfo>();
|
||||||
|
|
||||||
if (clusterDBDetails.FirstOrDefault() is DatabaseMetadata)
|
foreach (var dataSourceObjectMetadata in clusterDbDetails)
|
||||||
{
|
{
|
||||||
foreach (var dbDetail in clusterDBDetails)
|
var dbDetail = (DatabaseMetadata) dataSourceObjectMetadata;
|
||||||
|
long.TryParse(dbDetail.SizeInMB, out long sizeInMb);
|
||||||
|
|
||||||
|
var databaseInfo = new DatabaseInfo
|
||||||
{
|
{
|
||||||
DatabaseInfo databaseInfo = new DatabaseInfo();
|
Options =
|
||||||
long.TryParse(dbDetail.SizeInMB, out long sum_OriginalSize);
|
{
|
||||||
databaseInfo.Options["name"] = dbDetail.Name;
|
["name"] = dbDetail.Name,
|
||||||
databaseInfo.Options["sizeInMB"] = (sum_OriginalSize / (1024 * 1024)).ToString();
|
["sizeInMB"] = (sizeInMb / (1024 * 1024)).ToString()
|
||||||
databaseDetails.Add(databaseInfo);
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
databaseDetails.Add(databaseInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return databaseDetails;
|
return databaseDetails;
|
||||||
@@ -100,5 +110,17 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
|
|||||||
|
|
||||||
return databaseChildDetails;
|
return databaseChildDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DataSourceObjectMetadata CreateDataSourceObjectMetadata(DataSourceMetadataType datatype, string name, string urn)
|
||||||
|
{
|
||||||
|
return new DataSourceObjectMetadata
|
||||||
|
{
|
||||||
|
MetadataType = datatype,
|
||||||
|
MetadataTypeName = datatype.ToString(),
|
||||||
|
Name = name,
|
||||||
|
PrettyName = name,
|
||||||
|
Urn = $"{urn}",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.OperationalInsights;
|
||||||
|
using Microsoft.Azure.OperationalInsights.Models;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses;
|
||||||
|
using Microsoft.Rest;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor
|
||||||
|
{
|
||||||
|
public class MonitorClient
|
||||||
|
{
|
||||||
|
private readonly OperationalInsightsDataClient _queryClient;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly string _workspaceId;
|
||||||
|
private readonly string _token;
|
||||||
|
private const string BaseUri = "https://api.loganalytics.io/v1/workspaces";
|
||||||
|
private WorkspaceResponse _metadata;
|
||||||
|
|
||||||
|
public string WorkspaceId => _workspaceId;
|
||||||
|
|
||||||
|
public MonitorClient(string workspaceId, string token)
|
||||||
|
{
|
||||||
|
_workspaceId = workspaceId;
|
||||||
|
_token = token;
|
||||||
|
_httpClient = GetHttpClient(token);
|
||||||
|
_queryClient = new OperationalInsightsDataClient(new TokenCredentials(token))
|
||||||
|
{
|
||||||
|
WorkspaceId = workspaceId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkspaceResponse LoadMetadata(bool refresh = false)
|
||||||
|
{
|
||||||
|
if (_metadata != null && !refresh)
|
||||||
|
{
|
||||||
|
return _metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = $"{BaseUri}/{_workspaceId}/metadata";
|
||||||
|
var httpResponseMessage = _httpClient.GetAsync(url).Result;
|
||||||
|
var results = httpResponseMessage.Content.ReadAsStringAsync().Result;
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_metadata = JsonSerializer.Deserialize<WorkspaceResponse>(results, options);
|
||||||
|
return _metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient GetHttpClient(string token)
|
||||||
|
{
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<QueryResults> QueryAsync(string query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _queryClient.QueryAsync(query, cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryResults Query(string query)
|
||||||
|
{
|
||||||
|
return _queryClient.Query(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MonitorClient()
|
||||||
|
{
|
||||||
|
_httpClient.Dispose();
|
||||||
|
_queryClient.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor
|
||||||
|
{
|
||||||
|
public class MonitorDataSource : DataSourceBase
|
||||||
|
{
|
||||||
|
private readonly MonitorClient _monitorClient;
|
||||||
|
private readonly IntellisenseClientBase _intellisenseClient;
|
||||||
|
private WorkspaceResponse _metadata;
|
||||||
|
private Dictionary<string, List<DataSourceObjectMetadata>> _nodes;
|
||||||
|
|
||||||
|
public override string ClusterName => _monitorClient.WorkspaceId;
|
||||||
|
public override string DatabaseName { get; set; }
|
||||||
|
|
||||||
|
public MonitorDataSource(MonitorClient monitorClient, IntellisenseClientBase intellisenseClient)
|
||||||
|
{
|
||||||
|
_monitorClient = monitorClient;
|
||||||
|
_intellisenseClient = intellisenseClient;
|
||||||
|
_nodes = new Dictionary<string, List<DataSourceObjectMetadata>>();
|
||||||
|
_metadata = _monitorClient.LoadMetadata();
|
||||||
|
DataSourceType = DataSourceType.LogAnalytics;
|
||||||
|
SetupTableGroups(monitorClient.WorkspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupTableGroups(string workspaceId)
|
||||||
|
{
|
||||||
|
var workspace = _metadata.Workspaces.First(x => x.Id == workspaceId);
|
||||||
|
DatabaseName = $"{workspace.Name} ({workspace.Id})";
|
||||||
|
var metadataTableGroups = _metadata.TableGroups.ToDictionary(x => x.Id);
|
||||||
|
|
||||||
|
foreach (string workspaceTableGroup in workspace.TableGroups)
|
||||||
|
{
|
||||||
|
var tableGroup = metadataTableGroups[workspaceTableGroup];
|
||||||
|
|
||||||
|
var tableGroupNodeInfo =
|
||||||
|
MetadataFactory.CreateDataSourceObjectMetadata(DataSourceMetadataType.Folder, tableGroup.Name, $"{workspace.Id}.{tableGroup.Name}");
|
||||||
|
|
||||||
|
_nodes.SafeAdd($"{workspace.Id}", tableGroupNodeInfo);
|
||||||
|
|
||||||
|
SetupTables(tableGroupNodeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupTables(DataSourceObjectMetadata tableGroupNodeInfo)
|
||||||
|
{
|
||||||
|
var tables = GetNonEmptyTableNames();
|
||||||
|
var metadataTables = _metadata.Tables.ToDictionary(x => x.Name);
|
||||||
|
|
||||||
|
foreach (string tableName in tables)
|
||||||
|
{
|
||||||
|
var table = metadataTables[tableName];
|
||||||
|
|
||||||
|
var tableNodeInfo = MetadataFactory.CreateDataSourceObjectMetadata(DataSourceMetadataType.Table, table.Name,
|
||||||
|
$"{tableGroupNodeInfo.Urn}.{table.Name}");
|
||||||
|
|
||||||
|
_nodes.SafeAdd(tableGroupNodeInfo.Urn, tableNodeInfo);
|
||||||
|
|
||||||
|
SetupColumns(table, tableNodeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> GetNonEmptyTableNames()
|
||||||
|
{
|
||||||
|
string query = "union * | summarize count() by Type";
|
||||||
|
var results = _monitorClient.Query(query);
|
||||||
|
return results.Tables[0].Rows.Select(x => x[0]).OrderBy(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupColumns(TablesModel table, DataSourceObjectMetadata tableNodeInfo)
|
||||||
|
{
|
||||||
|
foreach (var column in table.Columns)
|
||||||
|
{
|
||||||
|
var columnNodeInfo = MetadataFactory.CreateDataSourceObjectMetadata(DataSourceMetadataType.Column, column.Name,
|
||||||
|
$"{tableNodeInfo.Urn}.{column.Name}");
|
||||||
|
|
||||||
|
_nodes.SafeAdd(tableNodeInfo.Urn, columnNodeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IDataReader> ExecuteQueryAsync(string query, CancellationToken cancellationToken, string databaseName = null)
|
||||||
|
{
|
||||||
|
var results = await _monitorClient.QueryAsync(query, cancellationToken);
|
||||||
|
return results.ToDataReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<IEnumerable<T>> ExecuteControlCommandAsync<T>(string command, bool throwOnError, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DiagnosticsInfo GetDiagnostics(DataSourceObjectMetadata parentMetadata)
|
||||||
|
{
|
||||||
|
return new DiagnosticsInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<DataSourceObjectMetadata> GetChildObjects(DataSourceObjectMetadata parentMetadata, bool includeSizeDetails = false)
|
||||||
|
{
|
||||||
|
// columns are always leaf nodes
|
||||||
|
if (parentMetadata.MetadataType == DataSourceMetadataType.Column)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<DataSourceObjectMetadata>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentMetadata.MetadataType == DataSourceMetadataType.Cluster && includeSizeDetails)
|
||||||
|
{
|
||||||
|
var child = _nodes[parentMetadata.Urn].FirstOrDefault();
|
||||||
|
return child == null ? Enumerable.Empty<DataSourceObjectMetadata>() : _nodes[child.Urn];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _nodes[parentMetadata.Urn].OrderBy(x => x.PrettyName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Refresh(bool includeDatabase)
|
||||||
|
{
|
||||||
|
// reset the data source
|
||||||
|
_nodes = new Dictionary<string, List<DataSourceObjectMetadata>>();
|
||||||
|
_metadata = _monitorClient.LoadMetadata();
|
||||||
|
SetupTableGroups(_monitorClient.WorkspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Refresh(DataSourceObjectMetadata objectMetadata)
|
||||||
|
{
|
||||||
|
Refresh(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateDatabase(string databaseName)
|
||||||
|
{
|
||||||
|
// LogAnalytics is treating the workspace name as the database name
|
||||||
|
var workspaceId = ParseWorkspaceId(databaseName);
|
||||||
|
_metadata = _monitorClient.LoadMetadata(true);
|
||||||
|
var workspace = _metadata.Workspaces.First(x => x.Id == workspaceId);
|
||||||
|
DatabaseName = $"{workspace.Name} ({workspace.Id})";
|
||||||
|
_intellisenseClient.UpdateDatabase(databaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ParseWorkspaceId(string workspace)
|
||||||
|
{
|
||||||
|
var regex = new Regex(@"(?<=\().+?(?=\))");
|
||||||
|
|
||||||
|
return regex.IsMatch(workspace)
|
||||||
|
? regex.Match(workspace).Value
|
||||||
|
: workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<bool> Exists()
|
||||||
|
{
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Exists(DataSourceObjectMetadata objectMetadata)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GenerateAlterFunctionScript(string functionName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GenerateExecuteFunctionScript(string functionName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ScriptFileMarker[] GetSemanticMarkers(ScriptParseInfo parseInfo, ScriptFile scriptFile, string queryText)
|
||||||
|
{
|
||||||
|
return _intellisenseClient.GetSemanticMarkers(parseInfo, scriptFile, queryText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DefinitionResult GetDefinition(string queryText, int index, int startLine, int startColumn, bool throwOnError = false)
|
||||||
|
{
|
||||||
|
return _intellisenseClient.GetDefinition(queryText, index, startLine, startColumn, throwOnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Hover GetHoverHelp(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false)
|
||||||
|
{
|
||||||
|
return _intellisenseClient.GetHoverHelp(scriptDocumentInfo, textPosition, throwOnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition, bool throwOnError = false)
|
||||||
|
{
|
||||||
|
return _intellisenseClient.GetAutoCompleteSuggestions(scriptDocumentInfo, textPosition, throwOnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ListDatabasesResponse GetDatabases(string serverName, bool includeDetails)
|
||||||
|
{
|
||||||
|
return new ListDatabasesResponse
|
||||||
|
{
|
||||||
|
DatabaseNames = new[]
|
||||||
|
{
|
||||||
|
DatabaseName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DatabaseInfo GetDatabaseInfo(string serverName, string databaseName)
|
||||||
|
{
|
||||||
|
return new DatabaseInfo
|
||||||
|
{
|
||||||
|
Options = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{"id", ClusterName},
|
||||||
|
{"name", DatabaseName}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using Kusto.Language.Symbols;
|
||||||
|
using Microsoft.Azure.OperationalInsights.Models;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor
|
||||||
|
{
|
||||||
|
public static class MonitorExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts QueryResults object into an IDataReader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryResults"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IDataReader ToDataReader(this QueryResults queryResults)
|
||||||
|
{
|
||||||
|
var resultTable = queryResults.Tables.FirstOrDefault();
|
||||||
|
|
||||||
|
if (resultTable == null)
|
||||||
|
{
|
||||||
|
return new DataTableReader(new DataTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataTable = new DataTable(resultTable.Name);
|
||||||
|
|
||||||
|
foreach (var column in resultTable.Columns)
|
||||||
|
{
|
||||||
|
dataTable.Columns.Add(column.Name, MapType(column.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var row in resultTable.Rows)
|
||||||
|
{
|
||||||
|
var dataRow = dataTable.NewRow();
|
||||||
|
|
||||||
|
for (int i = 0; i < row.Count; i++)
|
||||||
|
{
|
||||||
|
dataRow[i] = row[i] ?? DBNull.Value as object;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTable.Rows.Add(dataRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataTableReader(dataTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map Kusto type to .NET Type equivalent using scalar data types
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/scalar-data-types/">Here</seealso>
|
||||||
|
/// <param name="type">Kusto Type</param>
|
||||||
|
/// <returns>.NET Equivalent Type</returns>
|
||||||
|
private static Type MapType(string type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "bool": return Type.GetType("System.Boolean");
|
||||||
|
case "datetime": return Type.GetType("System.DateTime");
|
||||||
|
case "dynamic": return Type.GetType("System.Object");
|
||||||
|
case "guid": return Type.GetType("System.Guid");
|
||||||
|
case "int": return Type.GetType("System.Int32");
|
||||||
|
case "long": return Type.GetType("System.Int64");
|
||||||
|
case "real": return Type.GetType("System.Double");
|
||||||
|
case "string": return Type.GetType("System.String");
|
||||||
|
case "timespan": return Type.GetType("System.TimeSpan");
|
||||||
|
case "decimal": return Type.GetType("System.Data.SqlTypes.SqlDecimal");
|
||||||
|
|
||||||
|
default: return typeof(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScalarSymbol ToSymbolType(this string type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "bool": return ScalarTypes.Bool;
|
||||||
|
case "datetime": return ScalarTypes.DateTime;
|
||||||
|
case "dynamic": return ScalarTypes.Dynamic;
|
||||||
|
case "guid": return ScalarTypes.Guid;
|
||||||
|
case "int": return ScalarTypes.Int;
|
||||||
|
case "long": return ScalarTypes.Long;
|
||||||
|
case "real": return ScalarTypes.Real;
|
||||||
|
case "string": return ScalarTypes.String;
|
||||||
|
case "timespan": return ScalarTypes.TimeSpan;
|
||||||
|
case "decimal": return ScalarTypes.Decimal;
|
||||||
|
|
||||||
|
default: return ScalarTypes.String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Kusto.Language;
|
||||||
|
using Kusto.Language.Symbols;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor
|
||||||
|
{
|
||||||
|
public class MonitorIntellisenseClient : IntellisenseClientBase
|
||||||
|
{
|
||||||
|
private readonly MonitorClient _monitorClient;
|
||||||
|
|
||||||
|
public MonitorIntellisenseClient(MonitorClient monitorClient)
|
||||||
|
{
|
||||||
|
_monitorClient = monitorClient;
|
||||||
|
schemaState = LoadSchemaState(monitorClient.LoadMetadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlobalState LoadSchemaState(WorkspaceResponse metadata)
|
||||||
|
{
|
||||||
|
var globalState = GlobalState.Default;
|
||||||
|
|
||||||
|
var members = new List<Symbol>();
|
||||||
|
foreach (var table in metadata.Tables)
|
||||||
|
{
|
||||||
|
var columnSymbols = table.Columns.Select(x => new ColumnSymbol(x.Name, x.Type.ToSymbolType()));
|
||||||
|
|
||||||
|
var tableSymbol = new TableSymbol(table.Name, columnSymbols);
|
||||||
|
members.Add(tableSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseSymbol = new DatabaseSymbol(metadata.Workspaces.First().Id, members);
|
||||||
|
return globalState.WithDatabase(databaseSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateDatabase(string databaseName)
|
||||||
|
{
|
||||||
|
var workspace = _monitorClient.LoadMetadata();
|
||||||
|
schemaState = LoadSchemaState(workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models
|
||||||
|
{
|
||||||
|
public class ColumnsModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models
|
||||||
|
{
|
||||||
|
public class TableGroupsModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Source { get; set; }
|
||||||
|
public string[] Tables { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models
|
||||||
|
{
|
||||||
|
public class TablesModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string TimeSpanColumn { get; set; }
|
||||||
|
|
||||||
|
public ColumnsModel[] Columns { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models
|
||||||
|
{
|
||||||
|
public class WorkspacesModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Region { get; set; }
|
||||||
|
public string ResourceId { get; set; }
|
||||||
|
public string[] TableGroups { get; set; }
|
||||||
|
public string[] Tables { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses.Models;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor.Responses
|
||||||
|
{
|
||||||
|
public class WorkspaceResponse
|
||||||
|
{
|
||||||
|
public TableGroupsModel[] TableGroups { get; set; }
|
||||||
|
public TablesModel[] Tables { get; set; }
|
||||||
|
public WorkspacesModel[] Workspaces { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
_connectionDetails = connectionDetails;
|
_connectionDetails = connectionDetails;
|
||||||
_dataSourceFactory = dataSourceFactory;
|
_dataSourceFactory = dataSourceFactory;
|
||||||
_ownerUri = ownerUri;
|
_ownerUri = ownerUri;
|
||||||
_dataSource = dataSourceFactory.Create(DataSourceType.Kusto, connectionDetails, ownerUri);
|
_dataSource = dataSourceFactory.Create(connectionDetails, ownerUri);
|
||||||
|
|
||||||
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
||||||
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
||||||
@@ -191,7 +191,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
|||||||
{
|
{
|
||||||
_connectionRetryPolicy.ExecuteAction(() =>
|
_connectionRetryPolicy.ExecuteAction(() =>
|
||||||
{
|
{
|
||||||
_dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, _connectionDetails, _ownerUri);
|
_dataSource = _dataSourceFactory.Create(_connectionDetails, _ownerUri);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,9 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Composition;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
//using Kusto.Language;
|
|
||||||
//using Kusto.Language.Editor;
|
|
||||||
using Microsoft.SqlTools.Extensibility;
|
using Microsoft.SqlTools.Extensibility;
|
||||||
using Microsoft.SqlTools.Hosting;
|
using Microsoft.SqlTools.Hosting;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
@@ -26,7 +23,6 @@ using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
|
|||||||
namespace Microsoft.Kusto.ServiceLayer.Formatter
|
namespace Microsoft.Kusto.ServiceLayer.Formatter
|
||||||
{
|
{
|
||||||
|
|
||||||
[Export(typeof(IHostedService))]
|
|
||||||
public class TSqlFormatterService : HostedService<TSqlFormatterService>, IComposableService
|
public class TSqlFormatterService : HostedService<TSqlFormatterService>, IComposableService
|
||||||
{
|
{
|
||||||
private FormatterSettings settings;
|
private FormatterSettings settings;
|
||||||
@@ -38,8 +34,6 @@ namespace Microsoft.Kusto.ServiceLayer.Formatter
|
|||||||
settings = new FormatterSettings();
|
settings = new FormatterSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override void InitializeService(IProtocolEndpoint serviceHost)
|
public override void InitializeService(IProtocolEndpoint serviceHost)
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, "TSqlFormatter initialized");
|
Logger.Write(TraceEventType.Verbose, "TSqlFormatter initialized");
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
var scripter = serviceProvider.GetService<IScripter>();
|
var scripter = serviceProvider.GetService<IScripter>();
|
||||||
var dataSourceConnectionFactory = serviceProvider.GetService<IDataSourceConnectionFactory>();
|
var dataSourceConnectionFactory = serviceProvider.GetService<IDataSourceConnectionFactory>();
|
||||||
var connectedBindingQueue = serviceProvider.GetService<IConnectedBindingQueue>();
|
var connectedBindingQueue = serviceProvider.GetService<IConnectedBindingQueue>();
|
||||||
var dataSourceFactory = serviceProvider.GetService<IDataSourceFactory>();
|
|
||||||
|
|
||||||
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
|
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
|
||||||
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
|
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
|
||||||
@@ -81,7 +80,7 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue);
|
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue);
|
||||||
serviceProvider.RegisterSingleService(LanguageService.Instance);
|
serviceProvider.RegisterSingleService(LanguageService.Instance);
|
||||||
|
|
||||||
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue, dataSourceFactory);
|
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue);
|
||||||
serviceProvider.RegisterSingleService(ConnectionService.Instance);
|
serviceProvider.RegisterSingleService(ConnectionService.Instance);
|
||||||
|
|
||||||
CredentialService.Instance.InitializeService(serviceHost);
|
CredentialService.Instance.InitializeService(serviceHost);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bindingContext.BindingLock.Reset();
|
bindingContext.BindingLock.Reset();
|
||||||
bindingContext.DataSource = _dataSourceFactory.Create(DataSourceType.Kusto, connInfo.ConnectionDetails, connInfo.OwnerUri);
|
bindingContext.DataSource = _dataSourceFactory.Create(connInfo.ConnectionDetails, connInfo.OwnerUri);
|
||||||
bindingContext.BindingTimeout = DefaultBindingTimeout;
|
bindingContext.BindingTimeout = DefaultBindingTimeout;
|
||||||
bindingContext.IsConnected = true;
|
bindingContext.IsConnected = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -782,10 +782,10 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
|
|||||||
internal Hover GetHoverItem(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
|
internal Hover GetHoverItem(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
|
||||||
{
|
{
|
||||||
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(scriptFile.ClientUri);
|
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(scriptFile.ClientUri);
|
||||||
ConnectionInfo connInfo;
|
if (!ConnectionServiceInstance.TryFindConnection(scriptFile.ClientUri, out var connInfo))
|
||||||
ConnectionServiceInstance.TryFindConnection(
|
{
|
||||||
scriptFile.ClientUri,
|
return null;
|
||||||
out connInfo);
|
}
|
||||||
|
|
||||||
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null) // populate parseresult or check why it is used.
|
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null) // populate parseresult or check why it is used.
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||||
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
|
|
||||||
@@ -20,13 +19,9 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MetadataService
|
public sealed class MetadataService
|
||||||
{
|
{
|
||||||
private static readonly Lazy<MetadataService> LazyInstance = new Lazy<MetadataService>();
|
|
||||||
|
|
||||||
public static MetadataService Instance => LazyInstance.Value;
|
|
||||||
|
|
||||||
private static ConnectionService _connectionService;
|
private static ConnectionService _connectionService;
|
||||||
|
private static readonly Lazy<MetadataService> LazyInstance = new Lazy<MetadataService>();
|
||||||
internal Task MetadataListTask { get; private set; }
|
public static MetadataService Instance => LazyInstance.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the Metadata Service instance
|
/// Initializes the Metadata Service instance
|
||||||
@@ -42,42 +37,17 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle a metadata query request
|
/// Handle a metadata query request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal async Task HandleMetadataListRequest(
|
internal async Task HandleMetadataListRequest(MetadataQueryParams metadataParams, RequestContext<MetadataQueryResult> requestContext)
|
||||||
MetadataQueryParams metadataParams,
|
|
||||||
RequestContext<MetadataQueryResult> requestContext)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Func<Task> requestHandler = async () =>
|
List<ObjectMetadata> metadata = await Task.Run(() => LoadMetadata(metadataParams));
|
||||||
|
|
||||||
|
await requestContext.SendResult(new MetadataQueryResult
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
Metadata = metadata.ToArray()
|
||||||
_connectionService.TryFindConnection(metadataParams.OwnerUri, out connInfo);
|
|
||||||
|
|
||||||
var metadata = new List<ObjectMetadata>();
|
|
||||||
if (connInfo != null)
|
|
||||||
{
|
|
||||||
ReliableDataSourceConnection connection;
|
|
||||||
connInfo.TryGetConnection("Default", out connection);
|
|
||||||
IDataSource dataSource = connection.GetUnderlyingConnection();
|
|
||||||
|
|
||||||
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
|
|
||||||
DataSourceObjectMetadata databaseMetadata = MetadataFactory.CreateDatabaseMetadata(objectMetadata, connInfo.ConnectionDetails.DatabaseName);
|
|
||||||
|
|
||||||
IEnumerable<DataSourceObjectMetadata> databaseChildMetadataInfo = dataSource.GetChildObjects(databaseMetadata, true);
|
|
||||||
metadata = MetadataFactory.ConvertToObjectMetadata(databaseChildMetadataInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
await requestContext.SendResult(new MetadataQueryResult
|
|
||||||
{
|
|
||||||
Metadata = metadata.ToArray()
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
|
|
||||||
{
|
|
||||||
await requestContext.SendError(t.Exception.ToString());
|
|
||||||
});
|
});
|
||||||
MetadataListTask = task;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -85,6 +55,24 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ObjectMetadata> LoadMetadata(MetadataQueryParams metadataParams)
|
||||||
|
{
|
||||||
|
_connectionService.TryFindConnection(metadataParams.OwnerUri, out ConnectionInfo connInfo);
|
||||||
|
|
||||||
|
if (connInfo == null)
|
||||||
|
{
|
||||||
|
return new List<ObjectMetadata>();
|
||||||
|
}
|
||||||
|
|
||||||
|
connInfo.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
||||||
|
IDataSource dataSource = connection.GetUnderlyingConnection();
|
||||||
|
|
||||||
|
var clusterMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
|
||||||
|
var databaseMetadata = MetadataFactory.CreateDatabaseMetadata(clusterMetadata, connInfo.ConnectionDetails.DatabaseName);
|
||||||
|
var parentMetadata = dataSource.DataSourceType == DataSourceType.LogAnalytics ? clusterMetadata : databaseMetadata;
|
||||||
|
|
||||||
|
var databaseChildMetadataInfo = dataSource.GetChildObjects(parentMetadata, true);
|
||||||
|
return MetadataFactory.ConvertToObjectMetadata(databaseChildMetadataInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Azure.OperationalInsights" />
|
||||||
<PackageReference Include="Microsoft.SqlServer.DACFx" />
|
<PackageReference Include="Microsoft.SqlServer.DACFx" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
<PackageReference Include="System.Text.Encoding.CodePages" />
|
||||||
<PackageReference Include="Microsoft.Azure.Kusto.Data" />
|
<PackageReference Include="Microsoft.Azure.Kusto.Data" />
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
|
|||||||
NodeTypeDictionary.Add("Server", serverSet);
|
NodeTypeDictionary.Add("Server", serverSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static HashSet<string> FindNodePaths(ObjectExplorerService.ObjectExplorerSession objectExplorerSession, string typeName, string schema, string name, string databaseName, List<string> parentNames = null)
|
internal static HashSet<string> FindNodePaths(ObjectExplorerSession objectExplorerSession, string typeName, string schema, string name, string databaseName, List<string> parentNames = null)
|
||||||
{
|
{
|
||||||
if (TreeRoot == null)
|
if (TreeRoot == null)
|
||||||
{
|
{
|
||||||
@@ -86,7 +86,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
|
|||||||
return returnSet;
|
return returnSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashSet<string> GenerateNodePath(ObjectExplorerService.ObjectExplorerSession objectExplorerSession, Node currentNode, string databaseName, List<string> parentNames, string path)
|
private static HashSet<string> GenerateNodePath(ObjectExplorerSession objectExplorerSession, Node currentNode, string databaseName, List<string> parentNames, string path)
|
||||||
{
|
{
|
||||||
if (parentNames != null)
|
if (parentNames != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
parent != null ? parent.GetNodePath() : "", ex.Message,
|
parent != null ? parent.GetNodePath() : "", ex.Message,
|
||||||
ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
||||||
Logger.Write(TraceEventType.Error, error);
|
Logger.Write(TraceEventType.Error, error);
|
||||||
throw ex;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.Nodes
|
|||||||
string error = string.Format(CultureInfo.InvariantCulture, "Failed getting child objects. parent:{0} error:{1} inner:{2} stacktrace:{3}",
|
string error = string.Format(CultureInfo.InvariantCulture, "Failed getting child objects. parent:{0} error:{1} inner:{2} stacktrace:{3}",
|
||||||
parent != null ? parent.GetNodePath() : "", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
parent != null ? parent.GetNodePath() : "", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
||||||
Logger.Write(TraceEventType.Error, error);
|
Logger.Write(TraceEventType.Error, error);
|
||||||
throw ex;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Composition;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -24,8 +23,6 @@ using Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel;
|
|||||||
using Microsoft.Kusto.ServiceLayer.SqlContext;
|
using Microsoft.Kusto.ServiceLayer.SqlContext;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace;
|
using Microsoft.Kusto.ServiceLayer.Workspace;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||||
@@ -34,32 +31,29 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
/// A Service to support querying server and database information as an Object Explorer tree.
|
/// A Service to support querying server and database information as an Object Explorer tree.
|
||||||
/// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API.
|
/// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Export(typeof(IHostedService))]
|
|
||||||
public class ObjectExplorerService : HostedService<ObjectExplorerService>, IComposableService, IHostedService, IDisposable
|
public class ObjectExplorerService : HostedService<ObjectExplorerService>, IComposableService, IHostedService, IDisposable
|
||||||
{
|
{
|
||||||
private readonly IConnectedBindingQueue _connectedBindingQueue;
|
private readonly IConnectedBindingQueue _connectedBindingQueue;
|
||||||
internal const string uriPrefix = "objectexplorer://";
|
|
||||||
|
|
||||||
// Instance of the connection service, used to get the connection info for a given owner URI
|
// Instance of the connection service, used to get the connection info for a given owner URI
|
||||||
private ConnectionService connectionService;
|
private ConnectionService _connectionService;
|
||||||
private IProtocolEndpoint _serviceHost;
|
private IProtocolEndpoint _serviceHost;
|
||||||
private ConcurrentDictionary<string, ObjectExplorerSession> sessionMap;
|
private readonly ConcurrentDictionary<string, ObjectExplorerSession> _sessionMap;
|
||||||
private IMultiServiceProvider serviceProvider;
|
private IMultiServiceProvider _serviceProvider;
|
||||||
private string connectionName = "ObjectExplorer";
|
private string connectionName = "ObjectExplorer";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This timeout limits the amount of time that object explorer tasks can take to complete
|
/// This timeout limits the amount of time that object explorer tasks can take to complete
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ObjectExplorerSettings settings;
|
private ObjectExplorerSettings _settings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton constructor
|
/// Singleton constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ImportingConstructor]
|
|
||||||
public ObjectExplorerService(IConnectedBindingQueue connectedBindingQueue)
|
public ObjectExplorerService(IConnectedBindingQueue connectedBindingQueue)
|
||||||
{
|
{
|
||||||
_connectedBindingQueue = connectedBindingQueue;
|
_connectedBindingQueue = connectedBindingQueue;
|
||||||
sessionMap = new ConcurrentDictionary<string, ObjectExplorerSession>();
|
_sessionMap = new ConcurrentDictionary<string, ObjectExplorerSession>();
|
||||||
NodePathGenerator.Initialize();
|
NodePathGenerator.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +64,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new ReadOnlyCollection<string>(sessionMap.Keys.ToList());
|
return new ReadOnlyCollection<string>(_sessionMap.Keys.ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +76,12 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
public override void SetServiceProvider(IMultiServiceProvider provider)
|
public override void SetServiceProvider(IMultiServiceProvider provider)
|
||||||
{
|
{
|
||||||
Validate.IsNotNull(nameof(provider), provider);
|
Validate.IsNotNull(nameof(provider), provider);
|
||||||
serviceProvider = provider;
|
_serviceProvider = provider;
|
||||||
connectionService = provider.GetService<ConnectionService>();
|
_connectionService = provider.GetService<ConnectionService>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
connectionService.RegisterConnectedQueue(connectionName, _connectedBindingQueue);
|
_connectionService.RegisterConnectedQueue(connectionName, _connectedBindingQueue);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
@@ -112,7 +107,8 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
serviceHost.SetRequestHandler(RefreshRequest.Type, HandleRefreshRequest);
|
serviceHost.SetRequestHandler(RefreshRequest.Type, HandleRefreshRequest);
|
||||||
serviceHost.SetRequestHandler(CloseSessionRequest.Type, HandleCloseSessionRequest);
|
serviceHost.SetRequestHandler(CloseSessionRequest.Type, HandleCloseSessionRequest);
|
||||||
serviceHost.SetRequestHandler(FindNodesRequest.Type, HandleFindNodesRequest);
|
serviceHost.SetRequestHandler(FindNodesRequest.Type, HandleFindNodesRequest);
|
||||||
WorkspaceService<SqlToolsSettings> workspaceService = WorkspaceService;
|
|
||||||
|
WorkspaceService<SqlToolsSettings> workspaceService = _serviceProvider.GetService<WorkspaceService<SqlToolsSettings>>();
|
||||||
if (workspaceService != null)
|
if (workspaceService != null)
|
||||||
{
|
{
|
||||||
workspaceService.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
|
workspaceService.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
|
||||||
@@ -120,15 +116,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the workspace service. Note: should handle case where this is null in cases where unit tests do not set this up
|
|
||||||
/// </summary>
|
|
||||||
private WorkspaceService<SqlToolsSettings> WorkspaceService
|
|
||||||
{
|
|
||||||
get { return serviceProvider.GetService<WorkspaceService<SqlToolsSettings>>(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensure formatter settings are always up to date
|
/// Ensure formatter settings are always up to date
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -138,7 +125,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
EventContext eventContext)
|
EventContext eventContext)
|
||||||
{
|
{
|
||||||
// update the current settings to reflect any changes (assuming formatter settings exist)
|
// 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);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +171,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
string uri = expandParams.SessionId;
|
string uri = expandParams.SessionId;
|
||||||
ObjectExplorerSession session = null;
|
ObjectExplorerSession session = null;
|
||||||
if (!sessionMap.TryGetValue(uri, out session))
|
if (!_sessionMap.TryGetValue(uri, out session))
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
|
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
|
||||||
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
||||||
@@ -197,7 +184,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RunExpandTask(session, expandParams);
|
await RunExpandTask(session, expandParams);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -214,7 +201,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
string uri = refreshParams.SessionId;
|
string uri = refreshParams.SessionId;
|
||||||
ObjectExplorerSession session = null;
|
ObjectExplorerSession session = null;
|
||||||
if (!sessionMap.TryGetValue(uri, out session))
|
if (!_sessionMap.TryGetValue(uri, out session))
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
|
Logger.Write(TraceEventType.Verbose, $"Cannot expand object explorer node. Couldn't find session for uri. {uri} ");
|
||||||
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
||||||
@@ -226,7 +213,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RunExpandTask(session, refreshParams, true);
|
await RunExpandTask(session, refreshParams, true);
|
||||||
}
|
}
|
||||||
await context.SendResult(true);
|
await context.SendResult(true);
|
||||||
}
|
}
|
||||||
@@ -249,7 +236,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
string uri = closeSessionParams.SessionId;
|
string uri = closeSessionParams.SessionId;
|
||||||
ObjectExplorerSession session = null;
|
ObjectExplorerSession session = null;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (!sessionMap.TryGetValue(uri, out session))
|
if (!_sessionMap.TryGetValue(uri, out session))
|
||||||
{
|
{
|
||||||
Logger.Write(TraceEventType.Verbose, $"Cannot close object explorer session. Couldn't find session for uri. {uri} ");
|
Logger.Write(TraceEventType.Verbose, $"Cannot close object explorer session. Couldn't find session for uri. {uri} ");
|
||||||
}
|
}
|
||||||
@@ -282,17 +269,17 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
internal void CloseSession(string uri)
|
internal void CloseSession(string uri)
|
||||||
{
|
{
|
||||||
ObjectExplorerSession session;
|
ObjectExplorerSession session;
|
||||||
if (sessionMap.TryGetValue(uri, out session))
|
if (_sessionMap.TryGetValue(uri, out session))
|
||||||
{
|
{
|
||||||
// Remove the session from active sessions and disconnect
|
// Remove the session from active sessions and disconnect
|
||||||
if(sessionMap.TryRemove(session.Uri, out session))
|
if(_sessionMap.TryRemove(session.Uri, out session))
|
||||||
{
|
{
|
||||||
if (session != null && session.ConnectionInfo != null)
|
if (session != null && session.ConnectionInfo != null)
|
||||||
{
|
{
|
||||||
_connectedBindingQueue.RemoveBindingContext(session.ConnectionInfo);
|
_connectedBindingQueue.RemoveBindingContext(session.ConnectionInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connectionService.Disconnect(new DisconnectParams()
|
_connectionService.Disconnect(new DisconnectParams()
|
||||||
{
|
{
|
||||||
OwnerUri = uri
|
OwnerUri = uri
|
||||||
});
|
});
|
||||||
@@ -306,11 +293,10 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
if (connectionDetails != null && !string.IsNullOrEmpty(uri))
|
if (connectionDetails != null && !string.IsNullOrEmpty(uri))
|
||||||
{
|
{
|
||||||
Task task = CreateSessionAsync(connectionDetails, uri, cancellationTokenSource.Token);
|
Task task = CreateSessionAsync(connectionDetails, uri, cancellationTokenSource.Token);
|
||||||
CreateSessionTask = task;
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
||||||
settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout);
|
_settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout);
|
||||||
|
|
||||||
if (result != null && !result.IsCompleted)
|
if (result != null && !result.IsCompleted)
|
||||||
{
|
{
|
||||||
@@ -329,19 +315,10 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For tests only
|
|
||||||
/// </summary>
|
|
||||||
internal Task CreateSessionTask
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SessionCreatedParameters> CreateSessionAsync(ConnectionDetails connectionDetails, string uri, CancellationToken cancellationToken)
|
private async Task<SessionCreatedParameters> CreateSessionAsync(ConnectionDetails connectionDetails, string uri, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ObjectExplorerSession session;
|
ObjectExplorerSession session;
|
||||||
if (!sessionMap.TryGetValue(uri, out session))
|
if (!_sessionMap.TryGetValue(uri, out session))
|
||||||
{
|
{
|
||||||
// Establish a connection to the specified server/database
|
// Establish a connection to the specified server/database
|
||||||
session = await DoCreateSession(connectionDetails, uri);
|
session = await DoCreateSession(connectionDetails, uri);
|
||||||
@@ -395,7 +372,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int timeout = (int)TimeSpan.FromSeconds(settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout).TotalMilliseconds;
|
int timeout = (int)TimeSpan.FromSeconds(_settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout).TotalMilliseconds;
|
||||||
QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
|
QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
|
||||||
key: _connectedBindingQueue.AddConnectionContext(session.ConnectionInfo, false, connectionName, false),
|
key: _connectedBindingQueue.AddConnectionContext(session.ConnectionInfo, false, connectionName, false),
|
||||||
bindingTimeout: timeout,
|
bindingTimeout: timeout,
|
||||||
@@ -448,7 +425,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
ConnectionInfo connectionInfo;
|
ConnectionInfo connectionInfo;
|
||||||
ConnectionCompleteParams connectionResult = await Connect(connectParams, uri);
|
ConnectionCompleteParams connectionResult = await Connect(connectParams, uri);
|
||||||
if (!connectionService.TryFindConnection(uri, out connectionInfo))
|
if (!_connectionService.TryFindConnection(uri, out connectionInfo))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -459,17 +436,17 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int timeout = (int)TimeSpan.FromSeconds(settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout).TotalMilliseconds;
|
int timeout = (int)TimeSpan.FromSeconds(_settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout).TotalMilliseconds;
|
||||||
QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
|
QueueItem queueItem = _connectedBindingQueue.QueueBindingOperation(
|
||||||
key: _connectedBindingQueue.AddConnectionContext(connectionInfo, false, connectionName),
|
key: _connectedBindingQueue.AddConnectionContext(connectionInfo, false, connectionName),
|
||||||
bindingTimeout: timeout,
|
bindingTimeout: timeout,
|
||||||
waitForLockTimeout: timeout,
|
waitForLockTimeout: timeout,
|
||||||
bindOperation: (bindingContext, cancelToken) =>
|
bindOperation: (bindingContext, cancelToken) =>
|
||||||
{
|
{
|
||||||
session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider, bindingContext.DataSource, isDefaultOrSystemDatabase);
|
session = ObjectExplorerSession.CreateSession(connectionResult, _serviceProvider, bindingContext.DataSource, isDefaultOrSystemDatabase);
|
||||||
session.ConnectionInfo = connectionInfo;
|
session.ConnectionInfo = connectionInfo;
|
||||||
|
|
||||||
sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
_sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
||||||
return session;
|
return session;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -493,7 +470,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// open connection based on request details
|
// open connection based on request details
|
||||||
ConnectionCompleteParams result = await connectionService.Connect(connectParams);
|
ConnectionCompleteParams result = await _connectionService.Connect(connectParams);
|
||||||
connectionErrorMessage = result != null ? $"{result.Messages} error code:{result.ErrorNumber}" : string.Empty;
|
connectionErrorMessage = result != null ? $"{result.Messages} error code:{result.ErrorNumber}" : string.Empty;
|
||||||
if (result != null && !string.IsNullOrEmpty(result.ConnectionId))
|
if (result != null && !string.IsNullOrEmpty(result.ConnectionId))
|
||||||
{
|
{
|
||||||
@@ -537,15 +514,14 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
await _serviceHost.SendEvent(SessionDisconnectedNotification.Type, result);
|
await _serviceHost.SendEvent(SessionDisconnectedNotification.Type, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunExpandTask(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
|
private async Task RunExpandTask(ObjectExplorerSession session, ExpandParams expandParams, bool forceRefresh = false)
|
||||||
{
|
{
|
||||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
Task task = ExpandNodeAsync(session, expandParams, cancellationTokenSource.Token, forceRefresh);
|
Task task = ExpandNodeAsync(session, expandParams, cancellationTokenSource.Token, forceRefresh);
|
||||||
ExpandTask = task;
|
await Task.Run(async () =>
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
||||||
settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout);
|
_settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout);
|
||||||
|
|
||||||
if (result != null && !result.IsCompleted)
|
if (result != null && !result.IsCompleted)
|
||||||
{
|
{
|
||||||
@@ -575,15 +551,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For tests only
|
|
||||||
/// </summary>
|
|
||||||
internal Task ExpandTask
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, CancellationToken cancellationToken, bool forceRefresh = false)
|
private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, CancellationToken cancellationToken, bool forceRefresh = false)
|
||||||
{
|
{
|
||||||
ExpandResponse response = null;
|
ExpandResponse response = null;
|
||||||
@@ -628,7 +595,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
public List<TreeNode> FindNodes(string sessionId, string typeName, string schema, string name, string databaseName, List<string> parentNames = null)
|
public List<TreeNode> FindNodes(string sessionId, string typeName, string schema, string name, string databaseName, List<string> parentNames = null)
|
||||||
{
|
{
|
||||||
var nodes = new List<TreeNode>();
|
var nodes = new List<TreeNode>();
|
||||||
var oeSession = sessionMap.GetValueOrDefault(sessionId);
|
var oeSession = _sessionMap.GetValueOrDefault(sessionId);
|
||||||
if (oeSession == null)
|
if (oeSession == null)
|
||||||
{
|
{
|
||||||
return nodes;
|
return nodes;
|
||||||
@@ -672,7 +639,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
|
|
||||||
private string LookupUriFromQueueKey(string queueKey)
|
private string LookupUriFromQueueKey(string queueKey)
|
||||||
{
|
{
|
||||||
foreach (var session in this.sessionMap.Values)
|
foreach (var session in _sessionMap.Values)
|
||||||
{
|
{
|
||||||
var connInfo = session.ConnectionInfo;
|
var connInfo = session.ConnectionInfo;
|
||||||
if (connInfo != null)
|
if (connInfo != null)
|
||||||
@@ -686,39 +653,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ObjectExplorerSession
|
|
||||||
{
|
|
||||||
public ObjectExplorerSession(string uri, TreeNode root)
|
|
||||||
{
|
|
||||||
Validate.IsNotNullOrEmptyString("uri", uri);
|
|
||||||
Validate.IsNotNull("root", root);
|
|
||||||
Uri = uri;
|
|
||||||
Root = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Uri { get; private set; }
|
|
||||||
public TreeNode Root { get; private set; }
|
|
||||||
|
|
||||||
public ConnectionInfo ConnectionInfo { get; set; }
|
|
||||||
|
|
||||||
public string ErrorMessage { get; set; }
|
|
||||||
|
|
||||||
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider, IDataSource dataSource, bool isDefaultOrSystemDatabase)
|
|
||||||
{
|
|
||||||
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(dataSource.ClusterName);
|
|
||||||
ServerNode rootNode = new ServerNode(response, serviceProvider, dataSource, objectMetadata);
|
|
||||||
|
|
||||||
var session = new ObjectExplorerSession(response.OwnerUri, rootNode);
|
|
||||||
if (!isDefaultOrSystemDatabase)
|
|
||||||
{
|
|
||||||
DataSourceObjectMetadata databaseMetadata = MetadataFactory.CreateDatabaseMetadata(objectMetadata, response.ConnectionSummary.DatabaseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.ObjectExplorer.Nodes;
|
||||||
|
using Microsoft.SqlTools.Extensibility;
|
||||||
|
using Microsoft.SqlTools.Utility;
|
||||||
|
|
||||||
|
namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||||
|
{
|
||||||
|
internal class ObjectExplorerSession
|
||||||
|
{
|
||||||
|
internal ObjectExplorerSession(string uri, TreeNode root)
|
||||||
|
{
|
||||||
|
Validate.IsNotNullOrEmptyString("uri", uri);
|
||||||
|
Validate.IsNotNull("root", root);
|
||||||
|
Uri = uri;
|
||||||
|
Root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Uri { get; private set; }
|
||||||
|
public TreeNode Root { get; private set; }
|
||||||
|
|
||||||
|
public ConnectionInfo ConnectionInfo { get; set; }
|
||||||
|
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider,
|
||||||
|
IDataSource dataSource, bool isDefaultOrSystemDatabase)
|
||||||
|
{
|
||||||
|
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(dataSource.ClusterName);
|
||||||
|
var rootNode = new ServerNode(response, serviceProvider, dataSource, objectMetadata);
|
||||||
|
|
||||||
|
var session = new ObjectExplorerSession(response.OwnerUri, rootNode);
|
||||||
|
if (!isDefaultOrSystemDatabase)
|
||||||
|
{
|
||||||
|
DataSourceObjectMetadata databaseMetadata =
|
||||||
|
MetadataFactory.CreateDatabaseMetadata(objectMetadata, response.ConnectionSummary.DatabaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
|
internal static string ServiceName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main entry point into the SQL Tools API Service Layer
|
/// Main entry point into the SQL Tools API Service Layer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -28,6 +30,8 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServiceName = commandOptions.ServiceName;
|
||||||
|
|
||||||
string logFilePath = commandOptions.LogFilePath;
|
string logFilePath = commandOptions.LogFilePath;
|
||||||
if (string.IsNullOrWhiteSpace(logFilePath))
|
if (string.IsNullOrWhiteSpace(logFilePath))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||||
using Microsoft.SqlTools.Extensibility;
|
using Microsoft.SqlTools.Extensibility;
|
||||||
using Microsoft.SqlTools.Hosting;
|
using Microsoft.SqlTools.Hosting;
|
||||||
@@ -27,8 +28,6 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ServiceHost : ServiceHostBase
|
public sealed class ServiceHost : ServiceHostBase
|
||||||
{
|
{
|
||||||
public const string ProviderName = "KUSTO";
|
|
||||||
private const string ProviderDescription = "Microsoft Azure Data Explorer";
|
|
||||||
private const string ProviderProtocolVersion = "1.0";
|
private const string ProviderProtocolVersion = "1.0";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,8 +35,9 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
/// prior to the process shutting down.
|
/// prior to the process shutting down.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int ShutdownTimeoutInSeconds = 120;
|
private const int ShutdownTimeoutInSeconds = 120;
|
||||||
public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" };
|
|
||||||
private IMultiServiceProvider serviceProvider;
|
private static readonly string[] CompletionTriggerCharacters = new[] { ".", "-", ":", "\\", "[", "\"" };
|
||||||
|
private IMultiServiceProvider _serviceProvider;
|
||||||
|
|
||||||
#region Singleton Instance Code
|
#region Singleton Instance Code
|
||||||
|
|
||||||
@@ -69,11 +69,11 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return serviceProvider;
|
return _serviceProvider;
|
||||||
}
|
}
|
||||||
internal set
|
internal set
|
||||||
{
|
{
|
||||||
serviceProvider = value;
|
_serviceProvider = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,32 +196,31 @@ namespace Microsoft.Kusto.ServiceLayer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles a request for the capabilities request
|
/// Handles a request for the capabilities request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal async Task HandleCapabilitiesRequest(
|
private async Task HandleCapabilitiesRequest(CapabilitiesRequest initializeParams, RequestContext<CapabilitiesResult> requestContext)
|
||||||
CapabilitiesRequest initializeParams,
|
|
||||||
RequestContext<CapabilitiesResult> requestContext)
|
|
||||||
{
|
{
|
||||||
await requestContext.SendResult(
|
string providerName = DataSourceFactory.GetProviderName();
|
||||||
new CapabilitiesResult
|
string providerDescription = DataSourceFactory.GetProviderDescription();
|
||||||
|
|
||||||
|
var capabilitiesResult = new CapabilitiesResult
|
||||||
|
{
|
||||||
|
Capabilities = new DmpServerCapabilities
|
||||||
{
|
{
|
||||||
Capabilities = new DmpServerCapabilities
|
ProtocolVersion = ProviderProtocolVersion,
|
||||||
{
|
ProviderName = providerName,
|
||||||
ProtocolVersion = ServiceHost.ProviderProtocolVersion,
|
ProviderDisplayName = providerDescription,
|
||||||
ProviderName = ServiceHost.ProviderName,
|
ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(),
|
||||||
ProviderDisplayName = ServiceHost.ProviderDescription,
|
// AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions(), // TODOKusto: May need it later as its in SqlTools.ServiceLayer
|
||||||
ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(),
|
Features = FeaturesMetadataProviderHelper.CreateFeatureMetadataProviders()
|
||||||
// AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions(), // TODOKusto: May need it later as its in SqlTools.ServiceLayer
|
|
||||||
Features = FeaturesMetadataProviderHelper.CreateFeatureMetadataProviders()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
|
await requestContext.SendResult(capabilitiesResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the version request. Sends back the server version as result.
|
/// Handles the version request. Sends back the server version as result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static async Task HandleVersionRequest(
|
private static async Task HandleVersionRequest(object versionRequestParams, RequestContext<string> requestContext)
|
||||||
object versionRequestParams,
|
|
||||||
RequestContext<string> requestContext)
|
|
||||||
{
|
{
|
||||||
await requestContext.SendResult(serviceVersion.ToString());
|
await requestContext.SendResult(serviceVersion.ToString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.Hosting.Utility
|
namespace Microsoft.SqlTools.Hosting.Utility
|
||||||
{
|
{
|
||||||
@@ -61,6 +60,9 @@ namespace Microsoft.SqlTools.Hosting.Utility
|
|||||||
case "-help":
|
case "-help":
|
||||||
ShouldExit = true;
|
ShouldExit = true;
|
||||||
return;
|
return;
|
||||||
|
case "-service-name":
|
||||||
|
ServiceName = args[++i];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
|
|||||||
{
|
{
|
||||||
public class DataSourceFactoryTests
|
public class DataSourceFactoryTests
|
||||||
{
|
{
|
||||||
[TestCase(typeof(ArgumentException), "ConnectionString", "AzureAccountToken")]
|
[TestCase(typeof(ArgumentException), "ConnectionString", "", "AzureMFA")]
|
||||||
public void Create_Throws_Exceptions_For_InvalidParams(Type exceptionType,
|
[TestCase(typeof(ArgumentException), "ConnectionString", "", "dstsAuth")]
|
||||||
string connectionString,
|
public void Create_Throws_Exceptions_For_InvalidAzureAccountToken(Type exceptionType, string connectionString, string azureAccountToken, string authType)
|
||||||
string azureAccountToken)
|
|
||||||
{
|
{
|
||||||
|
Program.ServiceName = "Kusto";
|
||||||
var dataSourceFactory = new DataSourceFactory();
|
var dataSourceFactory = new DataSourceFactory();
|
||||||
var connectionDetails = new ConnectionDetails
|
var connectionDetails = new ConnectionDetails
|
||||||
{
|
{
|
||||||
ConnectionString = connectionString,
|
ConnectionString = connectionString,
|
||||||
AccountToken = azureAccountToken
|
AccountToken = azureAccountToken,
|
||||||
|
AuthenticationType = authType
|
||||||
};
|
};
|
||||||
Assert.Throws(exceptionType,
|
Assert.Throws(exceptionType, () => dataSourceFactory.Create(connectionDetails, ""));
|
||||||
() => dataSourceFactory.Create(DataSourceType.None, connectionDetails, ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
|
||||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
|
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices
|
|||||||
var dataSourceFactory = new Mock<IDataSourceFactory>();
|
var dataSourceFactory = new Mock<IDataSourceFactory>();
|
||||||
var dataSourceMock = new Mock<IDataSource>();
|
var dataSourceMock = new Mock<IDataSource>();
|
||||||
dataSourceFactory
|
dataSourceFactory
|
||||||
.Setup(x => x.Create(It.IsAny<DataSourceType>(), It.IsAny<ConnectionDetails>(), It.IsAny<string>()))
|
.Setup(x => x.Create(It.IsAny<ConnectionDetails>(), It.IsAny<string>()))
|
||||||
.Returns(dataSourceMock.Object);
|
.Returns(dataSourceMock.Object);
|
||||||
|
|
||||||
var connectedBindingQueue =
|
var connectedBindingQueue =
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||||
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||||
|
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||||
using Microsoft.Kusto.ServiceLayer.Metadata;
|
using Microsoft.Kusto.ServiceLayer.Metadata;
|
||||||
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
@@ -11,25 +17,42 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.Metadata
|
|||||||
public class MetadataServiceTests
|
public class MetadataServiceTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void HandleMetadataListRequest_Sets_MetadataListTask()
|
public async Task HandleMetadataListRequest_Sets_MetadataListTask()
|
||||||
{
|
{
|
||||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||||
var connectionServiceMock = new Mock<ConnectionService>();
|
var connectionServiceMock = new Mock<ConnectionService>();
|
||||||
var connectionFactoryMock = new Mock<IDataSourceConnectionFactory>();
|
var connectionFactoryMock = new Mock<IDataSourceConnectionFactory>();
|
||||||
|
var requestContextMock = new Mock<RequestContext<MetadataQueryResult>>();
|
||||||
|
requestContextMock.Setup(x => x.SendResult(It.IsAny<MetadataQueryResult>())).Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var dataSourceMock = new Mock<IDataSource>();
|
||||||
|
dataSourceMock.Setup(x => x.GetChildObjects(It.IsAny<DataSourceObjectMetadata>(), It.IsAny<bool>()))
|
||||||
|
.Returns(new List<DataSourceObjectMetadata> {new DataSourceObjectMetadata {PrettyName = "TestName"}});
|
||||||
|
|
||||||
|
var dataSourceFactoryMock = new Mock<IDataSourceFactory>();
|
||||||
|
dataSourceFactoryMock.Setup(x => x.Create(It.IsAny<ConnectionDetails>(), It.IsAny<string>()))
|
||||||
|
.Returns(dataSourceMock.Object);
|
||||||
|
|
||||||
|
var reliableDataSource = new ReliableDataSourceConnection(new ConnectionDetails(), RetryPolicyFactory.NoRetryPolicy,
|
||||||
|
RetryPolicyFactory.NoRetryPolicy, dataSourceFactoryMock.Object, "");
|
||||||
|
|
||||||
|
var connectionDetails = new ConnectionDetails
|
||||||
|
{
|
||||||
|
ServerName = "ServerName",
|
||||||
|
DatabaseName = "DatabaseName"
|
||||||
|
};
|
||||||
|
var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", connectionDetails);
|
||||||
|
connectionInfo.AddConnection(ConnectionType.Default, reliableDataSource);
|
||||||
|
|
||||||
var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails());
|
|
||||||
connectionServiceMock.Setup(x => x.TryFindConnection(It.IsAny<string>(), out connectionInfo));
|
connectionServiceMock.Setup(x => x.TryFindConnection(It.IsAny<string>(), out connectionInfo));
|
||||||
|
|
||||||
var metadataService = new MetadataService();
|
var metadataService = new MetadataService();
|
||||||
metadataService.InitializeService(serviceHostMock.Object, connectionServiceMock.Object);
|
metadataService.InitializeService(serviceHostMock.Object, connectionServiceMock.Object);
|
||||||
|
|
||||||
Assert.IsNull(metadataService.MetadataListTask);
|
await metadataService.HandleMetadataListRequest(new MetadataQueryParams(), requestContextMock.Object);
|
||||||
|
|
||||||
var task = metadataService.HandleMetadataListRequest(new MetadataQueryParams(),
|
requestContextMock.Verify(x => x.SendResult(It.Is<MetadataQueryResult>(result => result.Metadata.First().Name == "TestName")),
|
||||||
new RequestContext<MetadataQueryResult>());
|
Times.Once());
|
||||||
task.Wait();
|
|
||||||
|
|
||||||
Assert.IsNotNull(metadataService.MetadataListTask);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user