mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -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.SqlServer.Assessment" Version="[1.0.305]" />
|
||||
<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="nunit" Version="3.12.0" />
|
||||
|
||||
@@ -42,33 +42,22 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
|
||||
/// <summary>
|
||||
/// Handle get database info request
|
||||
/// </summary>
|
||||
private async Task HandleGetDatabaseInfoRequest(
|
||||
GetDatabaseInfoParams databaseParams,
|
||||
RequestContext<GetDatabaseInfoResponse> requestContext)
|
||||
private async Task HandleGetDatabaseInfoRequest(GetDatabaseInfoParams databaseParams, RequestContext<GetDatabaseInfoResponse> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
Func<Task> requestHandler = async () =>
|
||||
var infoResponse = await Task.Run(() =>
|
||||
{
|
||||
_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo);
|
||||
DatabaseInfo info = null;
|
||||
|
||||
if (connInfo != null)
|
||||
if (_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo))
|
||||
{
|
||||
info = GetDatabaseInfo(connInfo);
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new GetDatabaseInfoResponse()
|
||||
{
|
||||
DatabaseInfo = info
|
||||
});
|
||||
};
|
||||
|
||||
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
|
||||
{
|
||||
await requestContext.SendError(t.Exception.ToString());
|
||||
return new GetDatabaseInfoResponse {DatabaseInfo = info};
|
||||
});
|
||||
|
||||
|
||||
await requestContext.SendResult(infoResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -88,18 +77,9 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
|
||||
return null;
|
||||
}
|
||||
|
||||
ReliableDataSourceConnection connection;
|
||||
connInfo.TryGetConnection("Default", out connection);
|
||||
connInfo.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
||||
IDataSource dataSource = connection.GetUnderlyingConnection();
|
||||
DataSourceObjectMetadata objectMetadata =
|
||||
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);
|
||||
return dataSource.GetDatabaseInfo(connInfo.ConnectionDetails.ServerName, connInfo.ConnectionDetails.DatabaseName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,14 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Admin.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
@@ -56,8 +53,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
|
||||
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
|
||||
|
||||
private IDataSourceFactory _dataSourceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Map from script URIs to ConnectionInfo objects
|
||||
/// </summary>
|
||||
@@ -84,55 +79,9 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
|
||||
/// <summary>
|
||||
/// Service host object for sending/receiving requests/events.
|
||||
/// Internal for testing purposes.
|
||||
/// </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>
|
||||
/// 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>
|
||||
/// <returns>A ConnectionCompleteParams object upon validation error,
|
||||
/// null upon validation success</returns>
|
||||
public ConnectionCompleteParams ValidateConnectParams(ConnectParams connectionParams)
|
||||
private ConnectionCompleteParams ValidateConnectParams(ConnectParams connectionParams)
|
||||
{
|
||||
string paramValidationErrorMessage;
|
||||
if (connectionParams == null)
|
||||
{
|
||||
return new ConnectionCompleteParams
|
||||
@@ -192,7 +140,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
Messages = SR.ConnectionServiceConnectErrorNullParams
|
||||
};
|
||||
}
|
||||
if (!connectionParams.IsValid(out paramValidationErrorMessage))
|
||||
if (!connectionParams.IsValid(out string paramValidationErrorMessage))
|
||||
{
|
||||
return new ConnectionCompleteParams
|
||||
{
|
||||
@@ -208,7 +156,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Open a connection with the specified ConnectParams
|
||||
/// </summary>
|
||||
public virtual async Task<ConnectionCompleteParams> Connect(ConnectParams connectionParams)
|
||||
public async Task<ConnectionCompleteParams> Connect(ConnectParams connectionParams)
|
||||
{
|
||||
// Validate parameters
|
||||
ConnectionCompleteParams validationResults = ValidateConnectParams(connectionParams);
|
||||
@@ -222,9 +170,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
connectionParams.Connection.ApplicationName = GetApplicationNameWithFeature(connectionParams.Connection.ApplicationName, connectionParams.Purpose);
|
||||
// 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.
|
||||
ConnectionInfo connectionInfo;
|
||||
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);
|
||||
}
|
||||
@@ -278,7 +225,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
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);
|
||||
|
||||
return response.Token;
|
||||
@@ -759,56 +706,31 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// List all databases on the server specified
|
||||
/// </summary>
|
||||
public ListDatabasesResponse ListDatabases(ListDatabasesParams listDatabasesParams)
|
||||
private ListDatabasesResponse ListDatabases(ListDatabasesParams listDatabasesParams)
|
||||
{
|
||||
// Verify parameters
|
||||
var owner = listDatabasesParams.OwnerUri;
|
||||
if (string.IsNullOrEmpty(owner))
|
||||
if (string.IsNullOrEmpty(listDatabasesParams.OwnerUri))
|
||||
{
|
||||
throw new ArgumentException(SR.ConnectionServiceListDbErrorNullOwnerUri);
|
||||
}
|
||||
|
||||
// Use the existing connection as a base for the search
|
||||
ConnectionInfo info;
|
||||
if (!TryFindConnection(owner, out info))
|
||||
if (!TryFindConnection(listDatabasesParams.OwnerUri, out ConnectionInfo info))
|
||||
{
|
||||
throw new Exception(SR.ConnectionServiceListDbErrorNotConnected(owner));
|
||||
throw new Exception(SR.ConnectionServiceListDbErrorNotConnected(listDatabasesParams.OwnerUri));
|
||||
}
|
||||
|
||||
info.TryGetConnection(ConnectionType.Default, out ReliableDataSourceConnection connection);
|
||||
IDataSource dataSource = connection.GetUnderlyingConnection();
|
||||
|
||||
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
|
||||
|
||||
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;
|
||||
return dataSource.GetDatabases(info.ConnectionDetails.ServerName, listDatabasesParams.IncludeDetails.HasTrue());
|
||||
}
|
||||
|
||||
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
|
||||
IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
|
||||
IConnectedBindingQueue connectedBindingQueue)
|
||||
{
|
||||
ServiceHost = serviceHost;
|
||||
_serviceHost = serviceHost;
|
||||
_dataSourceConnectionFactory = dataSourceConnectionFactory;
|
||||
_dataSourceFactory = dataSourceFactory;
|
||||
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
|
||||
LockedDatabaseManager.ConnectionService = this;
|
||||
|
||||
@@ -845,15 +767,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <param name="connectParams"></param>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task HandleConnectRequest(
|
||||
ConnectParams connectParams,
|
||||
RequestContext<bool> requestContext)
|
||||
private async Task HandleConnectRequest(ConnectParams connectParams, RequestContext<bool> requestContext)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "HandleConnectRequest");
|
||||
|
||||
try
|
||||
{
|
||||
RunConnectRequestHandlerTask(connectParams);
|
||||
await Task.Run(async () => await RunConnectRequestHandlerTask(connectParams));
|
||||
await requestContext.SendResult(true);
|
||||
}
|
||||
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
|
||||
Task.Run(async () =>
|
||||
try
|
||||
{
|
||||
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
|
||||
ConnectionCompleteParams result = ValidateConnectParams(connectParams);
|
||||
if (result != null)
|
||||
{
|
||||
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);
|
||||
Messages = ex.ToString()
|
||||
};
|
||||
await _serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -915,15 +823,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Handle disconnect requests
|
||||
/// </summary>
|
||||
protected async Task HandleDisconnectRequest(
|
||||
DisconnectParams disconnectParams,
|
||||
RequestContext<bool> requestContext)
|
||||
private async Task HandleDisconnectRequest(DisconnectParams disconnectParams, RequestContext<bool> requestContext)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "HandleDisconnectRequest");
|
||||
|
||||
try
|
||||
{
|
||||
bool result = Instance.Disconnect(disconnectParams);
|
||||
bool result = Disconnect(disconnectParams);
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -936,15 +842,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Handle requests to list databases on the current server
|
||||
/// </summary>
|
||||
protected async Task HandleListDatabasesRequest(
|
||||
ListDatabasesParams listDatabasesParams,
|
||||
RequestContext<ListDatabasesResponse> requestContext)
|
||||
private async Task HandleListDatabasesRequest(ListDatabasesParams listDatabasesParams, RequestContext<ListDatabasesResponse> requestContext)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "ListDatabasesRequest");
|
||||
|
||||
try
|
||||
{
|
||||
ListDatabasesResponse result = ListDatabases(listDatabasesParams);
|
||||
var result = await Task.Run(() => ListDatabases(listDatabasesParams));
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1025,11 +929,10 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Handles a request to change the database for a connection
|
||||
/// </summary>
|
||||
public async Task HandleChangeDatabaseRequest(
|
||||
ChangeDatabaseParams changeDatabaseParams,
|
||||
RequestContext<bool> requestContext)
|
||||
private async Task HandleChangeDatabaseRequest(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>
|
||||
@@ -1037,60 +940,57 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// </summary>
|
||||
/// <param name="ownerUri">URI of the owner of the connection</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 info))
|
||||
if (!TryFindConnection(ownerUri, out ConnectionInfo info))
|
||||
{
|
||||
try
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
info.ConnectionDetails.DatabaseName = newDatabaseName;
|
||||
|
||||
foreach (string key in info.AllConnectionTypes)
|
||||
{
|
||||
info.ConnectionDetails.DatabaseName = newDatabaseName;
|
||||
|
||||
foreach (string key in info.AllConnectionTypes)
|
||||
ReliableDataSourceConnection conn;
|
||||
info.TryGetConnection(key, out conn);
|
||||
if (conn != null && conn.Database != newDatabaseName)
|
||||
{
|
||||
ReliableDataSourceConnection conn;
|
||||
info.TryGetConnection(key, out conn);
|
||||
if (conn != null && conn.Database != newDatabaseName)
|
||||
if (info.IsCloud && force)
|
||||
{
|
||||
if (info.IsCloud && force)
|
||||
{
|
||||
conn.Close();
|
||||
conn.Dispose();
|
||||
info.RemoveConnection(key);
|
||||
conn.Close();
|
||||
conn.Dispose();
|
||||
info.RemoveConnection(key);
|
||||
|
||||
// create a kusto connection instance
|
||||
ReliableDataSourceConnection connection = info.Factory.CreateDataSourceConnection(info.ConnectionDetails, ownerUri);
|
||||
connection.Open();
|
||||
info.AddConnection(key, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn.ChangeDatabase(newDatabaseName);
|
||||
}
|
||||
// create a kusto connection instance
|
||||
ReliableDataSourceConnection connection = info.Factory.CreateDataSourceConnection(info.ConnectionDetails, ownerUri);
|
||||
connection.Open();
|
||||
info.AddConnection(key, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn.ChangeDatabase(newDatabaseName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire a connection changed event
|
||||
ConnectionChangedParams parameters = new ConnectionChangedParams();
|
||||
IConnectionSummary summary = info.ConnectionDetails;
|
||||
parameters.Connection = summary.Clone();
|
||||
parameters.OwnerUri = ownerUri;
|
||||
ServiceHost.SendEvent(ConnectionChangedNotification.Type, parameters);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Write(
|
||||
TraceEventType.Error,
|
||||
string.Format(
|
||||
"Exception caught while trying to change database context to [{0}] for OwnerUri [{1}]. Exception:{2}",
|
||||
newDatabaseName,
|
||||
ownerUri,
|
||||
e.ToString())
|
||||
);
|
||||
}
|
||||
// Fire a connection changed event
|
||||
ConnectionChangedParams parameters = new ConnectionChangedParams();
|
||||
IConnectionSummary summary = info.ConnectionDetails;
|
||||
parameters.Connection = summary.Clone();
|
||||
parameters.OwnerUri = ownerUri;
|
||||
_serviceHost.SendEvent(ConnectionChangedNotification.Type, parameters);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Write(
|
||||
TraceEventType.Error,
|
||||
$"Exception caught while trying to change database context to [{newDatabaseName}] for OwnerUri [{ownerUri}]. Exception:{e}"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1129,12 +1029,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <param name="info"></param>
|
||||
private void HandleDisconnectTelemetry(ConnectionInfo connectionInfo)
|
||||
{
|
||||
if (ServiceHost != null)
|
||||
if (_serviceHost != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Send a telemetry notification for intellisense performance metrics
|
||||
ServiceHost.SendEvent(TelemetryNotification.Type, new TelemetryParams()
|
||||
_serviceHost.SendEvent(TelemetryNotification.Type, new TelemetryParams()
|
||||
{
|
||||
Params = new TelemetryProperties
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Data;
|
||||
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.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||
@@ -100,13 +102,16 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
public abstract CompletionItem[] GetAutoCompleteSuggestions(ScriptDocumentInfo scriptDocumentInfo, Position textPosition,
|
||||
bool throwOnError = false);
|
||||
|
||||
public abstract ListDatabasesResponse GetDatabases(string serverName, bool includeDetails);
|
||||
public abstract DatabaseInfo GetDatabaseInfo(string serverName, string databaseName);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DataSourceType DataSourceType { get; protected set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract string ClusterName { get; }
|
||||
|
||||
public abstract string DatabaseName { get; }
|
||||
public abstract string DatabaseName { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ using Microsoft.Kusto.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
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.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
@@ -16,8 +18,14 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
[Export(typeof(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)
|
||||
{
|
||||
case DataSourceType.Kusto:
|
||||
@@ -27,7 +35,12 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
var intellisenseClient = new KustoIntellisenseClient(kustoClient);
|
||||
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:
|
||||
|
||||
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)
|
||||
{
|
||||
if (connectionDetails.AuthenticationType == "dstsAuth" || connectionDetails.AuthenticationType == "AzureMFA")
|
||||
@@ -95,7 +113,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
}
|
||||
|
||||
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:
|
||||
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:
|
||||
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>
|
||||
/// An Operations Management Suite (OMS) Log Analytics workspace.
|
||||
/// </summary>
|
||||
OmsLogAnalytics
|
||||
LogAnalytics
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
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.LanguageServices;
|
||||
@@ -117,5 +119,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
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);
|
||||
ListDatabasesResponse GetDatabases(string serverName, bool includeDetails);
|
||||
DatabaseInfo GetDatabaseInfo(string serverName, string databaseName);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
{
|
||||
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.Tasks;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
public interface IKustoClient
|
||||
{
|
||||
@@ -17,7 +17,7 @@ using Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
public class KustoClient : IKustoClient
|
||||
{
|
||||
@@ -1,19 +1,21 @@
|
||||
// <copyright file="KustoDataSource.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All Rights Reserved.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Kusto.Cloud.Platform.Data;
|
||||
using Kusto.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.Metadata;
|
||||
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.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Kusto utilities.
|
||||
@@ -30,7 +33,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
public class KustoDataSource : DataSourceBase
|
||||
{
|
||||
private readonly IKustoClient _kustoClient;
|
||||
private readonly IIntellisenseClient _intellisenseClient;
|
||||
private readonly IntellisenseClientBase _intellisenseClient;
|
||||
|
||||
/// <summary>
|
||||
/// List of databases.
|
||||
@@ -57,7 +60,11 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
@@ -79,7 +86,7 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="IDataSource"/> class from being created.
|
||||
/// </summary>
|
||||
public KustoDataSource(IKustoClient kustoClient, IIntellisenseClient intellisenseClient)
|
||||
public KustoDataSource(IKustoClient kustoClient, IntellisenseClientBase intellisenseClient)
|
||||
{
|
||||
_kustoClient = kustoClient;
|
||||
_intellisenseClient = intellisenseClient;
|
||||
@@ -835,6 +842,44 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -4,34 +4,25 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Kusto.Language;
|
||||
using Kusto.Language.Editor;
|
||||
using Kusto.Language.Symbols;
|
||||
using Kusto.Language.Syntax;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
using Diagnostic = Kusto.Language.Diagnostic;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
public class KustoIntellisenseClient : IIntellisenseClient
|
||||
public class KustoIntellisenseClient : IntellisenseClientBase
|
||||
{
|
||||
private readonly IKustoClient _kustoClient;
|
||||
|
||||
/// <summary>
|
||||
/// SchemaState used for getting intellisense info.
|
||||
/// </summary>
|
||||
private GlobalState _schemaState;
|
||||
|
||||
public KustoIntellisenseClient(IKustoClient 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)
|
||||
@@ -237,135 +228,5 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
||||
|
||||
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 Kusto.Language;
|
||||
using Kusto.Language.Editor;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Intellisense
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
/// <summary>
|
||||
/// Kusto specific class for intellisense helper functions.
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource
|
||||
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
|
||||
{
|
||||
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>
|
||||
/// Add a range to a dictionary of ConcurrentDictionary. Adds range to existing IEnumerable within dictionary
|
||||
/// at the same key.
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
|
||||
public string Urn { get; set; }
|
||||
|
||||
public string SizeInMB { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,7 @@
|
||||
public class DatabaseMetadata : DataSourceObjectMetadata
|
||||
{
|
||||
public string ClusterName { get; set; }
|
||||
|
||||
public string SizeInMB { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -59,24 +59,34 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
|
||||
/// <summary>
|
||||
/// Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
|
||||
/// </summary>
|
||||
/// <param name="clusterDBDetails"></param>
|
||||
/// <param name="clusterDbDetails"></param>
|
||||
/// <returns></returns>
|
||||
public static List<DatabaseInfo> ConvertToDatabaseInfo(IEnumerable<DataSourceObjectMetadata> clusterDBDetails)
|
||||
public static List<DatabaseInfo> ConvertToDatabaseInfo(IEnumerable<DataSourceObjectMetadata> clusterDbDetails)
|
||||
{
|
||||
var databaseDetails = new List<DatabaseInfo>();
|
||||
|
||||
if (clusterDBDetails.FirstOrDefault() is DatabaseMetadata)
|
||||
if (clusterDbDetails.FirstOrDefault() is not DatabaseMetadata)
|
||||
{
|
||||
foreach (var dbDetail in clusterDBDetails)
|
||||
{
|
||||
DatabaseInfo databaseInfo = new DatabaseInfo();
|
||||
long.TryParse(dbDetail.SizeInMB, out long sum_OriginalSize);
|
||||
databaseInfo.Options["name"] = dbDetail.Name;
|
||||
databaseInfo.Options["sizeInMB"] = (sum_OriginalSize / (1024 * 1024)).ToString();
|
||||
databaseDetails.Add(databaseInfo);
|
||||
}
|
||||
return new List<DatabaseInfo>();
|
||||
}
|
||||
|
||||
var databaseDetails = new List<DatabaseInfo>();
|
||||
|
||||
foreach (var dataSourceObjectMetadata in clusterDbDetails)
|
||||
{
|
||||
var dbDetail = (DatabaseMetadata) dataSourceObjectMetadata;
|
||||
long.TryParse(dbDetail.SizeInMB, out long sizeInMb);
|
||||
|
||||
var databaseInfo = new DatabaseInfo
|
||||
{
|
||||
Options =
|
||||
{
|
||||
["name"] = dbDetail.Name,
|
||||
["sizeInMB"] = (sizeInMb / (1024 * 1024)).ToString()
|
||||
}
|
||||
};
|
||||
|
||||
databaseDetails.Add(databaseInfo);
|
||||
}
|
||||
|
||||
return databaseDetails;
|
||||
}
|
||||
|
||||
@@ -100,5 +110,17 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
|
||||
|
||||
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;
|
||||
_dataSourceFactory = dataSourceFactory;
|
||||
_ownerUri = ownerUri;
|
||||
_dataSource = dataSourceFactory.Create(DataSourceType.Kusto, connectionDetails, ownerUri);
|
||||
_dataSource = dataSourceFactory.Create(connectionDetails, ownerUri);
|
||||
|
||||
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
||||
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
|
||||
@@ -191,7 +191,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
{
|
||||
_connectionRetryPolicy.ExecuteAction(() =>
|
||||
{
|
||||
_dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, _connectionDetails, _ownerUri);
|
||||
_dataSource = _dataSourceFactory.Create(_connectionDetails, _ownerUri);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
//using Kusto.Language;
|
||||
//using Kusto.Language.Editor;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.Hosting;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
@@ -25,8 +22,7 @@ using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(IHostedService))]
|
||||
|
||||
public class TSqlFormatterService : HostedService<TSqlFormatterService>, IComposableService
|
||||
{
|
||||
private FormatterSettings settings;
|
||||
@@ -38,8 +34,6 @@ namespace Microsoft.Kusto.ServiceLayer.Formatter
|
||||
settings = new FormatterSettings();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void InitializeService(IProtocolEndpoint serviceHost)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "TSqlFormatter initialized");
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
var scripter = serviceProvider.GetService<IScripter>();
|
||||
var dataSourceConnectionFactory = serviceProvider.GetService<IDataSourceConnectionFactory>();
|
||||
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
|
||||
// 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);
|
||||
serviceProvider.RegisterSingleService(LanguageService.Instance);
|
||||
|
||||
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue, dataSourceFactory);
|
||||
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue);
|
||||
serviceProvider.RegisterSingleService(ConnectionService.Instance);
|
||||
|
||||
CredentialService.Instance.InitializeService(serviceHost);
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
|
||||
try
|
||||
{
|
||||
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.IsConnected = true;
|
||||
}
|
||||
|
||||
@@ -782,10 +782,10 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
|
||||
internal Hover GetHoverItem(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile)
|
||||
{
|
||||
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(scriptFile.ClientUri);
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(
|
||||
scriptFile.ClientUri,
|
||||
out connInfo);
|
||||
if (!ConnectionServiceInstance.TryFindConnection(scriptFile.ClientUri, out var connInfo))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
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.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||
|
||||
@@ -20,13 +19,9 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
|
||||
/// </summary>
|
||||
public sealed class MetadataService
|
||||
{
|
||||
private static readonly Lazy<MetadataService> LazyInstance = new Lazy<MetadataService>();
|
||||
|
||||
public static MetadataService Instance => LazyInstance.Value;
|
||||
|
||||
private static ConnectionService _connectionService;
|
||||
|
||||
internal Task MetadataListTask { get; private set; }
|
||||
private static readonly Lazy<MetadataService> LazyInstance = new Lazy<MetadataService>();
|
||||
public static MetadataService Instance => LazyInstance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Metadata Service instance
|
||||
@@ -42,42 +37,17 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
|
||||
/// <summary>
|
||||
/// Handle a metadata query request
|
||||
/// </summary>
|
||||
internal async Task HandleMetadataListRequest(
|
||||
MetadataQueryParams metadataParams,
|
||||
RequestContext<MetadataQueryResult> requestContext)
|
||||
internal async Task HandleMetadataListRequest(MetadataQueryParams metadataParams, RequestContext<MetadataQueryResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
Func<Task> requestHandler = async () =>
|
||||
List<ObjectMetadata> metadata = await Task.Run(() => LoadMetadata(metadataParams));
|
||||
|
||||
await requestContext.SendResult(new MetadataQueryResult
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
_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());
|
||||
Metadata = metadata.ToArray()
|
||||
});
|
||||
MetadataListTask = task;
|
||||
|
||||
}
|
||||
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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.OperationalInsights" />
|
||||
<PackageReference Include="Microsoft.SqlServer.DACFx" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
||||
<PackageReference Include="Microsoft.Azure.Kusto.Data" />
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
|
||||
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)
|
||||
{
|
||||
@@ -86,7 +86,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -361,7 +361,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.Nodes
|
||||
parent != null ? parent.GetNodePath() : "", ex.Message,
|
||||
ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
||||
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}",
|
||||
parent != null ? parent.GetNodePath() : "", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
|
||||
Logger.Write(TraceEventType.Error, error);
|
||||
throw ex;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -24,8 +23,6 @@ using Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel;
|
||||
using Microsoft.Kusto.ServiceLayer.SqlContext;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
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.
|
||||
/// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API.
|
||||
/// </summary>
|
||||
[Export(typeof(IHostedService))]
|
||||
public class ObjectExplorerService : HostedService<ObjectExplorerService>, IComposableService, IHostedService, IDisposable
|
||||
{
|
||||
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
|
||||
private ConnectionService connectionService;
|
||||
private ConnectionService _connectionService;
|
||||
private IProtocolEndpoint _serviceHost;
|
||||
private ConcurrentDictionary<string, ObjectExplorerSession> sessionMap;
|
||||
private IMultiServiceProvider serviceProvider;
|
||||
private readonly ConcurrentDictionary<string, ObjectExplorerSession> _sessionMap;
|
||||
private IMultiServiceProvider _serviceProvider;
|
||||
private string connectionName = "ObjectExplorer";
|
||||
|
||||
/// <summary>
|
||||
/// This timeout limits the amount of time that object explorer tasks can take to complete
|
||||
/// </summary>
|
||||
private ObjectExplorerSettings settings;
|
||||
private ObjectExplorerSettings _settings;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton constructor
|
||||
/// </summary>
|
||||
[ImportingConstructor]
|
||||
public ObjectExplorerService(IConnectedBindingQueue connectedBindingQueue)
|
||||
{
|
||||
_connectedBindingQueue = connectedBindingQueue;
|
||||
sessionMap = new ConcurrentDictionary<string, ObjectExplorerSession>();
|
||||
_sessionMap = new ConcurrentDictionary<string, ObjectExplorerSession>();
|
||||
NodePathGenerator.Initialize();
|
||||
}
|
||||
|
||||
@@ -70,7 +64,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
{
|
||||
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)
|
||||
{
|
||||
Validate.IsNotNull(nameof(provider), provider);
|
||||
serviceProvider = provider;
|
||||
connectionService = provider.GetService<ConnectionService>();
|
||||
_serviceProvider = provider;
|
||||
_connectionService = provider.GetService<ConnectionService>();
|
||||
|
||||
try
|
||||
{
|
||||
connectionService.RegisterConnectedQueue(connectionName, _connectedBindingQueue);
|
||||
_connectionService.RegisterConnectedQueue(connectionName, _connectedBindingQueue);
|
||||
|
||||
}
|
||||
catch(Exception ex)
|
||||
@@ -112,7 +107,8 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
serviceHost.SetRequestHandler(RefreshRequest.Type, HandleRefreshRequest);
|
||||
serviceHost.SetRequestHandler(CloseSessionRequest.Type, HandleCloseSessionRequest);
|
||||
serviceHost.SetRequestHandler(FindNodesRequest.Type, HandleFindNodesRequest);
|
||||
WorkspaceService<SqlToolsSettings> workspaceService = WorkspaceService;
|
||||
|
||||
WorkspaceService<SqlToolsSettings> workspaceService = _serviceProvider.GetService<WorkspaceService<SqlToolsSettings>>();
|
||||
if (workspaceService != null)
|
||||
{
|
||||
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>
|
||||
/// Ensure formatter settings are always up to date
|
||||
/// </summary>
|
||||
@@ -138,7 +125,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
EventContext eventContext)
|
||||
{
|
||||
// update the current settings to reflect any changes (assuming formatter settings exist)
|
||||
settings = newSettings?.SqlTools?.ObjectExplorer ?? settings;
|
||||
_settings = newSettings?.SqlTools?.ObjectExplorer ?? _settings;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
@@ -184,7 +171,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
|
||||
string uri = expandParams.SessionId;
|
||||
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} ");
|
||||
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
||||
@@ -197,7 +184,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
}
|
||||
else
|
||||
{
|
||||
RunExpandTask(session, expandParams);
|
||||
await RunExpandTask(session, expandParams);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -214,7 +201,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
|
||||
string uri = refreshParams.SessionId;
|
||||
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} ");
|
||||
await _serviceHost.SendEvent(ExpandCompleteNotification.Type, new ExpandResponse
|
||||
@@ -226,7 +213,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
}
|
||||
else
|
||||
{
|
||||
RunExpandTask(session, refreshParams, true);
|
||||
await RunExpandTask(session, refreshParams, true);
|
||||
}
|
||||
await context.SendResult(true);
|
||||
}
|
||||
@@ -249,7 +236,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
string uri = closeSessionParams.SessionId;
|
||||
ObjectExplorerSession session = null;
|
||||
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} ");
|
||||
}
|
||||
@@ -282,17 +269,17 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
internal void CloseSession(string uri)
|
||||
{
|
||||
ObjectExplorerSession session;
|
||||
if (sessionMap.TryGetValue(uri, out session))
|
||||
if (_sessionMap.TryGetValue(uri, out session))
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
_connectedBindingQueue.RemoveBindingContext(session.ConnectionInfo);
|
||||
}
|
||||
}
|
||||
connectionService.Disconnect(new DisconnectParams()
|
||||
_connectionService.Disconnect(new DisconnectParams()
|
||||
{
|
||||
OwnerUri = uri
|
||||
});
|
||||
@@ -306,11 +293,10 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
if (connectionDetails != null && !string.IsNullOrEmpty(uri))
|
||||
{
|
||||
Task task = CreateSessionAsync(connectionDetails, uri, cancellationTokenSource.Token);
|
||||
CreateSessionTask = task;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
||||
settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout);
|
||||
_settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout);
|
||||
|
||||
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)
|
||||
{
|
||||
ObjectExplorerSession session;
|
||||
if (!sessionMap.TryGetValue(uri, out session))
|
||||
if (!_sessionMap.TryGetValue(uri, out session))
|
||||
{
|
||||
// Establish a connection to the specified server/database
|
||||
session = await DoCreateSession(connectionDetails, uri);
|
||||
@@ -395,7 +372,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
{
|
||||
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(
|
||||
key: _connectedBindingQueue.AddConnectionContext(session.ConnectionInfo, false, connectionName, false),
|
||||
bindingTimeout: timeout,
|
||||
@@ -448,7 +425,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
|
||||
ConnectionInfo connectionInfo;
|
||||
ConnectionCompleteParams connectionResult = await Connect(connectParams, uri);
|
||||
if (!connectionService.TryFindConnection(uri, out connectionInfo))
|
||||
if (!_connectionService.TryFindConnection(uri, out connectionInfo))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -459,17 +436,17 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
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(
|
||||
key: _connectedBindingQueue.AddConnectionContext(connectionInfo, false, connectionName),
|
||||
bindingTimeout: timeout,
|
||||
waitForLockTimeout: timeout,
|
||||
bindOperation: (bindingContext, cancelToken) =>
|
||||
{
|
||||
session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider, bindingContext.DataSource, isDefaultOrSystemDatabase);
|
||||
session = ObjectExplorerSession.CreateSession(connectionResult, _serviceProvider, bindingContext.DataSource, isDefaultOrSystemDatabase);
|
||||
session.ConnectionInfo = connectionInfo;
|
||||
|
||||
sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
||||
_sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
||||
return session;
|
||||
});
|
||||
|
||||
@@ -493,7 +470,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
try
|
||||
{
|
||||
// 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;
|
||||
if (result != null && !string.IsNullOrEmpty(result.ConnectionId))
|
||||
{
|
||||
@@ -537,15 +514,14 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
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();
|
||||
Task task = ExpandNodeAsync(session, expandParams, cancellationTokenSource.Token, forceRefresh);
|
||||
ExpandTask = task;
|
||||
Task.Run(async () =>
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
ObjectExplorerTaskResult result = await RunTaskWithTimeout(task,
|
||||
settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout);
|
||||
_settings?.ExpandTimeout ?? ObjectExplorerSettings.DefaultExpandTimeout);
|
||||
|
||||
if (result != null && !result.IsCompleted)
|
||||
{
|
||||
@@ -575,15 +551,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var nodes = new List<TreeNode>();
|
||||
var oeSession = sessionMap.GetValueOrDefault(sessionId);
|
||||
var oeSession = _sessionMap.GetValueOrDefault(sessionId);
|
||||
if (oeSession == null)
|
||||
{
|
||||
return nodes;
|
||||
@@ -672,7 +639,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
|
||||
private string LookupUriFromQueueKey(string queueKey)
|
||||
{
|
||||
foreach (var session in this.sessionMap.Values)
|
||||
foreach (var session in _sessionMap.Values)
|
||||
{
|
||||
var connInfo = session.ConnectionInfo;
|
||||
if (connInfo != null)
|
||||
@@ -686,39 +653,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
|
||||
}
|
||||
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>
|
||||
internal class Program
|
||||
{
|
||||
internal static string ServiceName;
|
||||
|
||||
/// <summary>
|
||||
/// Main entry point into the SQL Tools API Service Layer
|
||||
/// </summary>
|
||||
@@ -28,6 +30,8 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceName = commandOptions.ServiceName;
|
||||
|
||||
string logFilePath = commandOptions.LogFilePath;
|
||||
if (string.IsNullOrWhiteSpace(logFilePath))
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.Hosting;
|
||||
@@ -27,8 +28,6 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
/// </summary>
|
||||
public sealed class ServiceHost : ServiceHostBase
|
||||
{
|
||||
public const string ProviderName = "KUSTO";
|
||||
private const string ProviderDescription = "Microsoft Azure Data Explorer";
|
||||
private const string ProviderProtocolVersion = "1.0";
|
||||
|
||||
/// <summary>
|
||||
@@ -36,8 +35,9 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
/// prior to the process shutting down.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
@@ -69,11 +69,11 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
{
|
||||
get
|
||||
{
|
||||
return serviceProvider;
|
||||
return _serviceProvider;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
serviceProvider = value;
|
||||
_serviceProvider = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,32 +196,31 @@ namespace Microsoft.Kusto.ServiceLayer
|
||||
/// <summary>
|
||||
/// Handles a request for the capabilities request
|
||||
/// </summary>
|
||||
internal async Task HandleCapabilitiesRequest(
|
||||
CapabilitiesRequest initializeParams,
|
||||
RequestContext<CapabilitiesResult> requestContext)
|
||||
private async Task HandleCapabilitiesRequest(CapabilitiesRequest initializeParams, RequestContext<CapabilitiesResult> requestContext)
|
||||
{
|
||||
await requestContext.SendResult(
|
||||
new CapabilitiesResult
|
||||
string providerName = DataSourceFactory.GetProviderName();
|
||||
string providerDescription = DataSourceFactory.GetProviderDescription();
|
||||
|
||||
var capabilitiesResult = new CapabilitiesResult
|
||||
{
|
||||
Capabilities = new DmpServerCapabilities
|
||||
{
|
||||
Capabilities = new DmpServerCapabilities
|
||||
{
|
||||
ProtocolVersion = ServiceHost.ProviderProtocolVersion,
|
||||
ProviderName = ServiceHost.ProviderName,
|
||||
ProviderDisplayName = ServiceHost.ProviderDescription,
|
||||
ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(),
|
||||
// AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions(), // TODOKusto: May need it later as its in SqlTools.ServiceLayer
|
||||
Features = FeaturesMetadataProviderHelper.CreateFeatureMetadataProviders()
|
||||
}
|
||||
ProtocolVersion = ProviderProtocolVersion,
|
||||
ProviderName = providerName,
|
||||
ProviderDisplayName = providerDescription,
|
||||
ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(),
|
||||
// AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions(), // TODOKusto: May need it later as its in SqlTools.ServiceLayer
|
||||
Features = FeaturesMetadataProviderHelper.CreateFeatureMetadataProviders()
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
await requestContext.SendResult(capabilitiesResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the version request. Sends back the server version as result.
|
||||
/// </summary>
|
||||
private static async Task HandleVersionRequest(
|
||||
object versionRequestParams,
|
||||
RequestContext<string> requestContext)
|
||||
private static async Task HandleVersionRequest(object versionRequestParams, RequestContext<string> requestContext)
|
||||
{
|
||||
await requestContext.SendResult(serviceVersion.ToString());
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.SqlTools.Hosting.Utility
|
||||
{
|
||||
@@ -61,6 +60,9 @@ namespace Microsoft.SqlTools.Hosting.Utility
|
||||
case "-help":
|
||||
ShouldExit = true;
|
||||
return;
|
||||
case "-service-name":
|
||||
ServiceName = args[++i];
|
||||
break;
|
||||
default:
|
||||
ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
||||
break;
|
||||
|
||||
@@ -11,19 +11,19 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
|
||||
{
|
||||
public class DataSourceFactoryTests
|
||||
{
|
||||
[TestCase(typeof(ArgumentException), "ConnectionString", "AzureAccountToken")]
|
||||
public void Create_Throws_Exceptions_For_InvalidParams(Type exceptionType,
|
||||
string connectionString,
|
||||
string azureAccountToken)
|
||||
[TestCase(typeof(ArgumentException), "ConnectionString", "", "AzureMFA")]
|
||||
[TestCase(typeof(ArgumentException), "ConnectionString", "", "dstsAuth")]
|
||||
public void Create_Throws_Exceptions_For_InvalidAzureAccountToken(Type exceptionType, string connectionString, string azureAccountToken, string authType)
|
||||
{
|
||||
Program.ServiceName = "Kusto";
|
||||
var dataSourceFactory = new DataSourceFactory();
|
||||
var connectionDetails = new ConnectionDetails
|
||||
{
|
||||
ConnectionString = connectionString,
|
||||
AccountToken = azureAccountToken
|
||||
AccountToken = azureAccountToken,
|
||||
AuthenticationType = authType
|
||||
};
|
||||
Assert.Throws(exceptionType,
|
||||
() => dataSourceFactory.Create(DataSourceType.None, connectionDetails, ""));
|
||||
Assert.Throws(exceptionType, () => dataSourceFactory.Create(connectionDetails, ""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -2,8 +2,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
using Moq;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices
|
||||
var dataSourceFactory = new Mock<IDataSourceFactory>();
|
||||
var dataSourceMock = new Mock<IDataSource>();
|
||||
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);
|
||||
|
||||
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.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
|
||||
using Microsoft.Kusto.ServiceLayer.Metadata;
|
||||
using Microsoft.Kusto.ServiceLayer.Metadata.Contracts;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -11,25 +17,42 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.Metadata
|
||||
public class MetadataServiceTests
|
||||
{
|
||||
[Test]
|
||||
public void HandleMetadataListRequest_Sets_MetadataListTask()
|
||||
public async Task HandleMetadataListRequest_Sets_MetadataListTask()
|
||||
{
|
||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
var connectionServiceMock = new Mock<ConnectionService>();
|
||||
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 connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", new ConnectionDetails());
|
||||
var connectionDetails = new ConnectionDetails
|
||||
{
|
||||
ServerName = "ServerName",
|
||||
DatabaseName = "DatabaseName"
|
||||
};
|
||||
var connectionInfo = new ConnectionInfo(connectionFactoryMock.Object, "", connectionDetails);
|
||||
connectionInfo.AddConnection(ConnectionType.Default, reliableDataSource);
|
||||
|
||||
connectionServiceMock.Setup(x => x.TryFindConnection(It.IsAny<string>(), out connectionInfo));
|
||||
|
||||
var metadataService = new MetadataService();
|
||||
metadataService.InitializeService(serviceHostMock.Object, connectionServiceMock.Object);
|
||||
|
||||
Assert.IsNull(metadataService.MetadataListTask);
|
||||
|
||||
var task = metadataService.HandleMetadataListRequest(new MetadataQueryParams(),
|
||||
new RequestContext<MetadataQueryResult>());
|
||||
task.Wait();
|
||||
|
||||
Assert.IsNotNull(metadataService.MetadataListTask);
|
||||
|
||||
await metadataService.HandleMetadataListRequest(new MetadataQueryParams(), requestContextMock.Object);
|
||||
|
||||
requestContextMock.Verify(x => x.SendResult(It.Is<MetadataQueryResult>(result => result.Metadata.First().Name == "TestName")),
|
||||
Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user