From 1f7da97e6c896aeb97da1ecfc6eba791182a5c5e Mon Sep 17 00:00:00 2001
From: Justin M <63619224+JustinMDotNet@users.noreply.github.com>
Date: Fri, 25 Jun 2021 21:40:45 -0700
Subject: [PATCH] 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
---
Packages.props | 1 +
.../Admin/AdminService.cs | 36 +--
.../Connection/ConnectionService.cs | 256 ++++++------------
.../DataSource/DataSourceBase.cs | 7 +-
.../DataSource/DataSourceFactory.cs | 38 ++-
.../DataSource/DataSourceType.cs | 2 +-
.../DataSource/IDataSource.cs | 4 +
.../DataSource/IDataSourceFactory.cs | 2 +-
.../Intellisense/IIntellisenseClient.cs | 15 -
.../Intellisense/IntellisenseClientBase.cs | 141 ++++++++++
.../DataSource/{ => Kusto}/IKustoClient.cs | 2 +-
.../DataSource/{ => Kusto}/KustoClient.cs | 2 +-
.../DataSource/{ => Kusto}/KustoDataSource.cs | 59 +++-
.../KustoIntellisenseClient.cs | 151 +----------
.../KustoIntellisenseHelper.cs | 3 +-
.../{ => Kusto}/KustoResultsReader.cs | 2 +-
.../DataSource/KustoQueryUtils.cs | 12 +
.../Metadata/DataSourceObjectMetadata.cs | 2 +-
.../DataSource/Metadata/DatabaseMetadata.cs | 2 +
.../DataSource/Metadata/MetadataFactory.cs | 48 +++-
.../DataSource/Monitor/MonitorClient.cs | 78 ++++++
.../DataSource/Monitor/MonitorDataSource.cs | 223 +++++++++++++++
.../DataSource/Monitor/MonitorExtensions.cs | 91 +++++++
.../Monitor/MonitorIntellisenseClient.cs | 43 +++
.../Monitor/Responses/Models/ColumnsModel.cs | 9 +
.../Responses/Models/TableGroupsModel.cs | 10 +
.../Monitor/Responses/Models/TablesModel.cs | 13 +
.../Responses/Models/WorkspacesModel.cs | 12 +
.../Monitor/Responses/WorkspaceResponse.cs | 11 +
.../ReliableDataSourceConnection.cs | 4 +-
.../Formatter/TSqlFormatterService.cs | 8 +-
.../HostLoader.cs | 3 +-
.../LanguageServices/ConnectedBindingQueue.cs | 2 +-
.../LanguageServices/LanguageService.cs | 8 +-
.../Metadata/MetadataService.cs | 66 ++---
.../Microsoft.Kusto.ServiceLayer.csproj | 1 +
.../DataSourceModel/NodePathGenerator.cs | 4 +-
.../ObjectExplorer/Nodes/TreeNode.cs | 4 +-
.../ObjectExplorer/ObjectExplorerService.cs | 134 +++------
.../ObjectExplorer/ObjectExplorerSession.cs | 45 +++
src/Microsoft.Kusto.ServiceLayer/Program.cs | 4 +
.../ServiceHost.cs | 47 ++--
.../Utility/CommandOptions.cs | 4 +-
.../DataSource/DataSourceFactoryTests.cs | 14 +-
.../KustoIntellisenseClientTests.cs | 2 +-
.../KustoIntellisenseHelperTests.cs | 1 +
.../DataSource/KustoClientTests.cs | 2 +-
.../ConnectedBindingQueueTests.cs | 2 +-
.../Metadata/MetadataServiceTests.cs | 43 ++-
49 files changed, 1070 insertions(+), 603 deletions(-)
delete mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IIntellisenseClient.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IntellisenseClientBase.cs
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{ => Kusto}/IKustoClient.cs (95%)
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{ => Kusto}/KustoClient.cs (99%)
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{ => Kusto}/KustoDataSource.cs (93%)
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{Intellisense => Kusto}/KustoIntellisenseClient.cs (55%)
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{Intellisense => Kusto}/KustoIntellisenseHelper.cs (97%)
rename src/Microsoft.Kusto.ServiceLayer/DataSource/{ => Kusto}/KustoResultsReader.cs (96%)
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorClient.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorDataSource.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorExtensions.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorIntellisenseClient.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/ColumnsModel.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TableGroupsModel.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TablesModel.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/WorkspacesModel.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/WorkspaceResponse.cs
create mode 100644 src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerSession.cs
diff --git a/Packages.props b/Packages.props
index 3e6fee77..507de312 100644
--- a/Packages.props
+++ b/Packages.props
@@ -25,6 +25,7 @@
+
diff --git a/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs b/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
index eba00e04..ef7015b8 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Admin/AdminService.cs
@@ -42,33 +42,22 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
///
/// Handle get database info request
///
- private async Task HandleGetDatabaseInfoRequest(
- GetDatabaseInfoParams databaseParams,
- RequestContext requestContext)
+ private async Task HandleGetDatabaseInfoRequest(GetDatabaseInfoParams databaseParams, RequestContext requestContext)
{
try
{
- Func 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 metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
- var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
-
- List databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
-
- return databaseInfo.ElementAtOrDefault(0);
+ return dataSource.GetDatabaseInfo(connInfo.ConnectionDetails.ServerName, connInfo.ConnectionDetails.DatabaseName);
}
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
index 4d4c8987..c8ccfdb5 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
@@ -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 connectedQueues = new ConcurrentDictionary();
- private IDataSourceFactory _dataSourceFactory;
-
///
/// Map from script URIs to ConnectionInfo objects
///
@@ -84,55 +79,9 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
/// Service host object for sending/receiving requests/events.
- /// Internal for testing purposes.
///
- internal IProtocolEndpoint ServiceHost { get; set; }
+ private IProtocolEndpoint _serviceHost;
- ///
- /// Gets the connection queue
- ///
- internal IConnectedBindingQueue ConnectionQueue
- {
- get
- {
- return this.GetConnectedQueue("Default");
- }
- }
-
-
- ///
- /// Default constructor should be private since it's a singleton class, but we need a constructor
- /// for use in unit test mocking.
- ///
- public ConnectionService()
- {
- }
-
- ///
- /// Returns a connection queue for given type
- ///
- ///
- ///
- public IConnectedBindingQueue GetConnectedQueue(string type)
- {
- IConnectedBindingQueue connectedBindingQueue;
- if (connectedQueues.TryGetValue(type, out connectedBindingQueue))
- {
- return connectedBindingQueue;
- }
- return null;
- }
-
- ///
- /// Returns all the connection queues
- ///
- public IEnumerable ConnectedQueues
- {
- get
- {
- return this.connectedQueues.Values;
- }
- }
///
/// Register a new connection queue if not already registered
@@ -182,9 +131,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// The params to validate
/// A ConnectionCompleteParams object upon validation error,
/// null upon validation success
- 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
///
/// Open a connection with the specified ConnectParams
///
- public virtual async Task Connect(ConnectParams connectionParams)
+ public async Task 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
///
/// List all databases on the server specified
///
- 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 databaseMetadataInfo = dataSource.GetChildObjects(objectMetadata, true);
- List metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
- response.Databases = metadata.ToArray();
-
- return response;
- }
-
- IEnumerable 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
///
///
///
- protected async Task HandleConnectRequest(
- ConnectParams connectParams,
- RequestContext requestContext)
+ private async Task HandleConnectRequest(ConnectParams connectParams, RequestContext 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);
+ }
}
///
@@ -915,15 +823,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
/// Handle disconnect requests
///
- protected async Task HandleDisconnectRequest(
- DisconnectParams disconnectParams,
- RequestContext requestContext)
+ private async Task HandleDisconnectRequest(DisconnectParams disconnectParams, RequestContext 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
///
/// Handle requests to list databases on the current server
///
- protected async Task HandleListDatabasesRequest(
- ListDatabasesParams listDatabasesParams,
- RequestContext requestContext)
+ private async Task HandleListDatabasesRequest(ListDatabasesParams listDatabasesParams, RequestContext 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
///
/// Handles a request to change the database for a connection
///
- public async Task HandleChangeDatabaseRequest(
- ChangeDatabaseParams changeDatabaseParams,
- RequestContext requestContext)
+ private async Task HandleChangeDatabaseRequest(ChangeDatabaseParams changeDatabaseParams, RequestContext 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);
}
///
@@ -1037,60 +940,57 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
/// URI of the owner of the connection
/// Name of the database to change the connection to
- 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;
}
///
@@ -1129,12 +1029,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
///
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
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceBase.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceBase.cs
index aff69352..26bb1762 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceBase.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceBase.cs
@@ -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);
+
///
public DataSourceType DataSourceType { get; protected set; }
///
public abstract string ClusterName { get; }
- public abstract string DatabaseName { get; }
+ public abstract string DatabaseName { get; set; }
#endregion
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
index b22dd108..d76f324d 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
@@ -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;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceType.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceType.cs
index 911a9116..2688211d 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceType.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceType.cs
@@ -23,6 +23,6 @@
///
/// An Operations Management Suite (OMS) Log Analytics workspace.
///
- OmsLogAnalytics
+ LogAnalytics
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSource.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSource.cs
index 13f777a2..fe95e70b 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSource.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSource.cs
@@ -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);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSourceFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSourceFactory.cs
index 8763dcde..978f1ef4 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSourceFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/IDataSourceFactory.cs
@@ -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);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IIntellisenseClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IIntellisenseClient.cs
deleted file mode 100644
index 9c373ab6..00000000
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IIntellisenseClient.cs
+++ /dev/null
@@ -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);
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IntellisenseClientBase.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IntellisenseClientBase.cs
new file mode 100644
index 00000000..7108a2b7
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/IntellisenseClientBase.cs
@@ -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();
+
+ foreach (var codeBlock in script.Blocks)
+ {
+ parseResult.AddRange(codeBlock.Service.GetDiagnostics());
+ }
+
+ parseInfo.ParseResult = parseResult;
+
+ if (!parseResult.Any())
+ {
+ return Array.Empty();
+ }
+
+ // build a list of Kusto script file markers from the errors.
+ var markers = new List();
+
+ 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();
+ 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/IKustoClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/IKustoClient.cs
similarity index 95%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/IKustoClient.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/IKustoClient.cs
index 9be38305..2cd2245f 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/IKustoClient.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/IKustoClient.cs
@@ -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
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
similarity index 99%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/KustoClient.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
index f3603b83..fd914217 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoClient.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
@@ -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
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoDataSource.cs
similarity index 93%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoDataSource.cs
index c4a9c8d7..4bfc29e1 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoDataSource.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoDataSource.cs
@@ -1,19 +1,21 @@
//
// Copyright (c) Microsoft. All Rights Reserved.
//
+
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
{
///
/// 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;
///
/// List of databases.
@@ -57,7 +60,11 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
///
private ConcurrentDictionary> _functionMetadata = new ConcurrentDictionary>();
- 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
///
/// Prevents a default instance of the class from being created.
///
- 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 databaseMetadataInfo = GetChildObjects(objectMetadata, true);
+ List metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
+
+ return new ListDatabasesResponse
+ {
+ Databases = metadata.ToArray()
+ };
+ }
+
+ IEnumerable 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 = MetadataFactory.ConvertToDatabaseInfo(metadata);
+ return databaseInfo.ElementAtOrDefault(0);
+ }
+
#endregion
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseClient.cs
similarity index 55%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseClient.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseClient.cs
index f321caa6..38f9007d 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseClient.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseClient.cs
@@ -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;
- ///
- /// SchemaState used for getting intellisense info.
- ///
- 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();
-
- foreach (var codeBlock in script.Blocks)
- {
- parseResult.AddRange(codeBlock.Service.GetDiagnostics());
- }
-
- parseInfo.ParseResult = parseResult;
-
- if (!parseResult.Any())
- {
- return Array.Empty();
- }
-
- // build a list of Kusto script file markers from the errors.
- var markers = new List();
-
- 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();
- 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;
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseHelper.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseHelper.cs
similarity index 97%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseHelper.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseHelper.cs
index 764c7543..093ba4b9 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Intellisense/KustoIntellisenseHelper.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoIntellisenseHelper.cs
@@ -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
{
///
/// Kusto specific class for intellisense helper functions.
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoResultsReader.cs
similarity index 96%
rename from src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs
rename to src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoResultsReader.cs
index 09fff1b1..e775692b 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoResultsReader.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoResultsReader.cs
@@ -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
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoQueryUtils.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoQueryUtils.cs
index 63984df3..fcf1e796 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoQueryUtils.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/KustoQueryUtils.cs
@@ -92,6 +92,18 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
}
}
+ public static void SafeAdd(this Dictionary> dictionary, string key, DataSourceObjectMetadata node)
+ {
+ if (dictionary.ContainsKey(key))
+ {
+ dictionary[key].Add(node);
+ }
+ else
+ {
+ dictionary[key] = new List {node};
+ }
+ }
+
///
/// Add a range to a dictionary of ConcurrentDictionary. Adds range to existing IEnumerable within dictionary
/// at the same key.
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DataSourceObjectMetadata.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DataSourceObjectMetadata.cs
index 97d98c5a..91be060f 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DataSourceObjectMetadata.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DataSourceObjectMetadata.cs
@@ -15,6 +15,6 @@
public string Urn { get; set; }
- public string SizeInMB { get; set; }
+
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DatabaseMetadata.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DatabaseMetadata.cs
index 1b6e072a..83bfaea6 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DatabaseMetadata.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/DatabaseMetadata.cs
@@ -6,5 +6,7 @@
public class DatabaseMetadata : DataSourceObjectMetadata
{
public string ClusterName { get; set; }
+
+ public string SizeInMB { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
index 83336624..c4897aa5 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Metadata/MetadataFactory.cs
@@ -59,24 +59,34 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Metadata
///
/// Converts database details shown on cluster manage dashboard to DatabaseInfo type. Add DataSourceType as param if required to show different properties
///
- ///
+ ///
///
- public static List ConvertToDatabaseInfo(IEnumerable clusterDBDetails)
+ public static List ConvertToDatabaseInfo(IEnumerable clusterDbDetails)
{
- var databaseDetails = new List();
-
- 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();
}
+ var databaseDetails = new List();
+
+ 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}",
+ };
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorClient.cs
new file mode 100644
index 00000000..07af32db
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorClient.cs
@@ -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(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 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorDataSource.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorDataSource.cs
new file mode 100644
index 00000000..33d3f662
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorDataSource.cs
@@ -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> _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>();
+ _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 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 ExecuteQueryAsync(string query, CancellationToken cancellationToken, string databaseName = null)
+ {
+ var results = await _monitorClient.QueryAsync(query, cancellationToken);
+ return results.ToDataReader();
+ }
+
+ public override Task> ExecuteControlCommandAsync(string command, bool throwOnError, CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override DiagnosticsInfo GetDiagnostics(DataSourceObjectMetadata parentMetadata)
+ {
+ return new DiagnosticsInfo();
+ }
+
+ public override IEnumerable GetChildObjects(DataSourceObjectMetadata parentMetadata, bool includeSizeDetails = false)
+ {
+ // columns are always leaf nodes
+ if (parentMetadata.MetadataType == DataSourceMetadataType.Column)
+ {
+ return Enumerable.Empty();
+ }
+
+ if (parentMetadata.MetadataType == DataSourceMetadataType.Cluster && includeSizeDetails)
+ {
+ var child = _nodes[parentMetadata.Urn].FirstOrDefault();
+ return child == null ? Enumerable.Empty() : _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>();
+ _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 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
+ {
+ {"id", ClusterName},
+ {"name", DatabaseName}
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorExtensions.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorExtensions.cs
new file mode 100644
index 00000000..15de0d56
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorExtensions.cs
@@ -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
+ {
+ ///
+ /// Converts QueryResults object into an IDataReader
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Map Kusto type to .NET Type equivalent using scalar data types
+ ///
+ /// Here
+ /// Kusto Type
+ /// .NET Equivalent Type
+ 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorIntellisenseClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorIntellisenseClient.cs
new file mode 100644
index 00000000..664a6e94
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/MonitorIntellisenseClient.cs
@@ -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();
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/ColumnsModel.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/ColumnsModel.cs
new file mode 100644
index 00000000..76edbf26
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/ColumnsModel.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TableGroupsModel.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TableGroupsModel.cs
new file mode 100644
index 00000000..541a5baf
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TableGroupsModel.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TablesModel.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TablesModel.cs
new file mode 100644
index 00000000..c4e671be
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/TablesModel.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/WorkspacesModel.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/WorkspacesModel.cs
new file mode 100644
index 00000000..82d84909
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/Models/WorkspacesModel.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/WorkspaceResponse.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/WorkspaceResponse.cs
new file mode 100644
index 00000000..fac43ac2
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Monitor/Responses/WorkspaceResponse.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
index 56483e43..4a045f87 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/ReliableDataSourceConnection.cs
@@ -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);
});
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Formatter/TSqlFormatterService.cs b/src/Microsoft.Kusto.ServiceLayer/Formatter/TSqlFormatterService.cs
index faeee98d..6feec8fb 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Formatter/TSqlFormatterService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Formatter/TSqlFormatterService.cs
@@ -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, 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");
diff --git a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
index 40c472a8..cd6e40df 100644
--- a/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/HostLoader.cs
@@ -70,7 +70,6 @@ namespace Microsoft.Kusto.ServiceLayer
var scripter = serviceProvider.GetService();
var dataSourceConnectionFactory = serviceProvider.GetService();
var connectedBindingQueue = serviceProvider.GetService();
- var dataSourceFactory = serviceProvider.GetService();
// 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);
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
index c7e8e683..4475f94d 100644
--- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
@@ -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;
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
index f756d489..1d00b0e8 100644
--- a/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/LanguageServices/LanguageService.cs
@@ -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.
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
index e14aa695..2898cf26 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Metadata/MetadataService.cs
@@ -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
///
public sealed class MetadataService
{
- private static readonly Lazy LazyInstance = new Lazy();
-
- public static MetadataService Instance => LazyInstance.Value;
-
private static ConnectionService _connectionService;
-
- internal Task MetadataListTask { get; private set; }
+ private static readonly Lazy LazyInstance = new Lazy();
+ public static MetadataService Instance => LazyInstance.Value;
///
/// Initializes the Metadata Service instance
@@ -42,42 +37,17 @@ namespace Microsoft.Kusto.ServiceLayer.Metadata
///
/// Handle a metadata query request
///
- internal async Task HandleMetadataListRequest(
- MetadataQueryParams metadataParams,
- RequestContext requestContext)
+ internal async Task HandleMetadataListRequest(MetadataQueryParams metadataParams, RequestContext requestContext)
{
try
{
- Func requestHandler = async () =>
+ List metadata = await Task.Run(() => LoadMetadata(metadataParams));
+
+ await requestContext.SendResult(new MetadataQueryResult
{
- ConnectionInfo connInfo;
- _connectionService.TryFindConnection(metadataParams.OwnerUri, out connInfo);
-
- var metadata = new List();
- 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 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 LoadMetadata(MetadataQueryParams metadataParams)
+ {
+ _connectionService.TryFindConnection(metadataParams.OwnerUri, out ConnectionInfo connInfo);
+
+ if (connInfo == null)
+ {
+ return new List();
+ }
+
+ 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);
+ }
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
index b5564549..c8f5cd44 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
+++ b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/DataSourceModel/NodePathGenerator.cs b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/DataSourceModel/NodePathGenerator.cs
index 059fce41..f487089e 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/DataSourceModel/NodePathGenerator.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/DataSourceModel/NodePathGenerator.cs
@@ -50,7 +50,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
NodeTypeDictionary.Add("Server", serverSet);
}
- internal static HashSet FindNodePaths(ObjectExplorerService.ObjectExplorerSession objectExplorerSession, string typeName, string schema, string name, string databaseName, List parentNames = null)
+ internal static HashSet FindNodePaths(ObjectExplorerSession objectExplorerSession, string typeName, string schema, string name, string databaseName, List parentNames = null)
{
if (TreeRoot == null)
{
@@ -86,7 +86,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer.DataSourceModel
return returnSet;
}
- private static HashSet GenerateNodePath(ObjectExplorerService.ObjectExplorerSession objectExplorerSession, Node currentNode, string databaseName, List parentNames, string path)
+ private static HashSet GenerateNodePath(ObjectExplorerSession objectExplorerSession, Node currentNode, string databaseName, List parentNames, string path)
{
if (parentNames != null)
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
index 8ffacbab..56566a55 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs
@@ -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;
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
index a1073fc1..131ff685 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs
@@ -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.
///
- [Export(typeof(IHostedService))]
public class ObjectExplorerService : HostedService, 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 sessionMap;
- private IMultiServiceProvider serviceProvider;
+ private readonly ConcurrentDictionary _sessionMap;
+ private IMultiServiceProvider _serviceProvider;
private string connectionName = "ObjectExplorer";
///
/// This timeout limits the amount of time that object explorer tasks can take to complete
///
- private ObjectExplorerSettings settings;
+ private ObjectExplorerSettings _settings;
///
/// Singleton constructor
///
- [ImportingConstructor]
public ObjectExplorerService(IConnectedBindingQueue connectedBindingQueue)
{
_connectedBindingQueue = connectedBindingQueue;
- sessionMap = new ConcurrentDictionary();
+ _sessionMap = new ConcurrentDictionary();
NodePathGenerator.Initialize();
}
@@ -70,7 +64,7 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
{
get
{
- return new ReadOnlyCollection(sessionMap.Keys.ToList());
+ return new ReadOnlyCollection(_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();
+ _serviceProvider = provider;
+ _connectionService = provider.GetService();
+
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 workspaceService = WorkspaceService;
+
+ WorkspaceService workspaceService = _serviceProvider.GetService>();
if (workspaceService != null)
{
workspaceService.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
@@ -120,15 +116,6 @@ namespace Microsoft.Kusto.ServiceLayer.ObjectExplorer
}
- ///
- /// Gets the workspace service. Note: should handle case where this is null in cases where unit tests do not set this up
- ///
- private WorkspaceService WorkspaceService
- {
- get { return serviceProvider.GetService>(); }
- }
-
-
///
/// Ensure formatter settings are always up to date
///
@@ -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
}
}
- ///
- /// For tests only
- ///
- internal Task CreateSessionTask
- {
- get;
- private set;
- }
-
private async Task 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;
}
- ///
- /// For tests only
- ///
- 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 FindNodes(string sessionId, string typeName, string schema, string name, string databaseName, List parentNames = null)
{
var nodes = new List();
- 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;
- }
-
- }
}
}
diff --git a/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerSession.cs b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerSession.cs
new file mode 100644
index 00000000..b0f510b7
--- /dev/null
+++ b/src/Microsoft.Kusto.ServiceLayer/ObjectExplorer/ObjectExplorerSession.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Kusto.ServiceLayer/Program.cs b/src/Microsoft.Kusto.ServiceLayer/Program.cs
index 34abf395..9eda3d4a 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Program.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Program.cs
@@ -14,6 +14,8 @@ namespace Microsoft.Kusto.ServiceLayer
///
internal class Program
{
+ internal static string ServiceName;
+
///
/// Main entry point into the SQL Tools API Service Layer
///
@@ -28,6 +30,8 @@ namespace Microsoft.Kusto.ServiceLayer
return;
}
+ ServiceName = commandOptions.ServiceName;
+
string logFilePath = commandOptions.LogFilePath;
if (string.IsNullOrWhiteSpace(logFilePath))
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs b/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
index d6f66dc4..8cdf6c56 100644
--- a/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/ServiceHost.cs
@@ -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
///
public sealed class ServiceHost : ServiceHostBase
{
- public const string ProviderName = "KUSTO";
- private const string ProviderDescription = "Microsoft Azure Data Explorer";
private const string ProviderProtocolVersion = "1.0";
///
@@ -36,8 +35,9 @@ namespace Microsoft.Kusto.ServiceLayer
/// prior to the process shutting down.
///
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
///
/// Handles a request for the capabilities request
///
- internal async Task HandleCapabilitiesRequest(
- CapabilitiesRequest initializeParams,
- RequestContext requestContext)
+ private async Task HandleCapabilitiesRequest(CapabilitiesRequest initializeParams, RequestContext 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);
}
///
/// Handles the version request. Sends back the server version as result.
///
- private static async Task HandleVersionRequest(
- object versionRequestParams,
- RequestContext requestContext)
+ private static async Task HandleVersionRequest(object versionRequestParams, RequestContext requestContext)
{
await requestContext.SendResult(serviceVersion.ToString());
}
diff --git a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs
index 3ffde1ad..ee99604b 100644
--- a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs
+++ b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs
@@ -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;
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
index 6d403b3b..24cae278 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
@@ -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]
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseClientTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseClientTests.cs
index 42295401..8c2ba4be 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseClientTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseClientTests.cs
@@ -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;
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs
index ec24b83e..7c0c87ea 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceIntellisense/KustoIntellisenseHelperTests.cs
@@ -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;
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
index 06e1bd02..10e3a189 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
@@ -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
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs
index dd8c4c11..3f4b59c3 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/LanguageServices/ConnectedBindingQueueTests.cs
@@ -100,7 +100,7 @@ namespace Microsoft.Kusto.ServiceLayer.UnitTests.LanguageServices
var dataSourceFactory = new Mock();
var dataSourceMock = new Mock();
dataSourceFactory
- .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Setup(x => x.Create(It.IsAny(), It.IsAny()))
.Returns(dataSourceMock.Object);
var connectedBindingQueue =
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs
index 2a115fb2..658ec2e2 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/Metadata/MetadataServiceTests.cs
@@ -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();
var connectionServiceMock = new Mock();
var connectionFactoryMock = new Mock();
+ var requestContextMock = new Mock>();
+ requestContextMock.Setup(x => x.SendResult(It.IsAny())).Returns(Task.CompletedTask);
+
+ var dataSourceMock = new Mock();
+ dataSourceMock.Setup(x => x.GetChildObjects(It.IsAny(), It.IsAny()))
+ .Returns(new List {new DataSourceObjectMetadata {PrettyName = "TestName"}});
+
+ var dataSourceFactoryMock = new Mock();
+ dataSourceFactoryMock.Setup(x => x.Create(It.IsAny(), It.IsAny()))
+ .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(), out connectionInfo));
var metadataService = new MetadataService();
metadataService.InitializeService(serviceHostMock.Object, connectionServiceMock.Object);
-
- Assert.IsNull(metadataService.MetadataListTask);
-
- var task = metadataService.HandleMetadataListRequest(new MetadataQueryParams(),
- new RequestContext());
- task.Wait();
-
- Assert.IsNotNull(metadataService.MetadataListTask);
+
+ await metadataService.HandleMetadataListRequest(new MetadataQueryParams(), requestContextMock.Object);
+
+ requestContextMock.Verify(x => x.SendResult(It.Is(result => result.Metadata.First().Name == "TestName")),
+ Times.Once());
}
}
}
\ No newline at end of file