3278 Kusto Unit Tests - Part 1 (#1057)

* 3278 Added unit tests in MetadataFactoryTests and Microsoft.Kusto.ServiceLayer.UnitTests project

* 3278 Removed todo and changed unit test to validate megabytes

* 3278 Added file and unit tests in AutoCompleteHelperTests.cs

* 3278 Removed unused functions from Kusto > ScriptAsScriptingOperation

* 3278 Added unit tests for DataSourceFactory

* 3278 Refactored AdminService to pass in ConnectionService rather than through instance variable. Added unit test for AdminServiceTests

* 3278 Refactored DataSourceFactory to not have static functions for future unit tests

* 3278 Re-added properties that were flagged as unused but are being used by ADS in ReliableDataSourceConnection.cs

* 3278 Re-added properties that were flagged as unused but are being used by ADS in ReliableDataSourceConnection.cs

* adding pipeline to execute tests (#1062)

* 3278 Converted GetDefaultAutoComplete and GetDefaultSemanticMarkers to static functions in DataSourceFactory. Removed unused constructor in ScriptFile. Added positive unit tests for both functions

* undoing release version bump

* adding additional configs

* adressing feedback

* Correcting path in csproj

Co-authored-by: Jorge Berumen <52225468+joberume@users.noreply.github.com>
Co-authored-by: joberume <jberumen3@miners.utep.edu>
This commit is contained in:
Justin M
2020-08-31 11:11:12 -07:00
committed by GitHub
parent 07700560a6
commit 1577177153
26 changed files with 565 additions and 379 deletions

View File

@@ -21,50 +21,28 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
/// </summary>
public class AdminService
{
private static readonly Lazy<AdminService> instance = new Lazy<AdminService>(() => new AdminService());
private static readonly Lazy<AdminService> _instance = new Lazy<AdminService>(() => new AdminService());
private static ConnectionService connectionService = null;
/// <summary>
/// Internal for testing purposes only
/// </summary>
internal static ConnectionService ConnectionServiceInstance
{
get
{
if (AdminService.connectionService == null)
{
AdminService.connectionService = ConnectionService.Instance;
}
return AdminService.connectionService;
}
set
{
AdminService.connectionService = value;
}
}
private static ConnectionService _connectionService;
/// <summary>
/// Gets the singleton instance object
/// </summary>
public static AdminService Instance
{
get { return instance.Value; }
}
public static AdminService Instance => _instance.Value;
/// <summary>
/// Initializes the service instance
/// </summary>
public void InitializeService(ServiceHost serviceHost)
public void InitializeService(ServiceHost serviceHost, ConnectionService connectionService)
{
serviceHost.SetRequestHandler(GetDatabaseInfoRequest.Type, HandleGetDatabaseInfoRequest);
_connectionService = connectionService;
}
/// <summary>
/// Handle get database info request
/// </summary>
internal async Task HandleGetDatabaseInfoRequest(
private async Task HandleGetDatabaseInfoRequest(
GetDatabaseInfoParams databaseParams,
RequestContext<GetDatabaseInfoResponse> requestContext)
{
@@ -72,10 +50,7 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
{
Func<Task> requestHandler = async () =>
{
ConnectionInfo connInfo;
AdminService.ConnectionServiceInstance.TryFindConnection(
databaseParams.OwnerUri,
out connInfo);
_connectionService.TryFindConnection(databaseParams.OwnerUri, out var connInfo);
DatabaseInfo info = null;
if (connInfo != null)
@@ -100,29 +75,31 @@ namespace Microsoft.Kusto.ServiceLayer.Admin
await requestContext.SendError(ex.ToString());
}
}
/// <summary>
/// Return database info for a specific database
/// </summary>
/// <param name="connInfo"></param>
/// <returns></returns>
internal DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
public DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
{
if(!string.IsNullOrEmpty(connInfo.ConnectionDetails.DatabaseName)){
ReliableDataSourceConnection connection;
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
List<DataSourceObjectMetadata> metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
return databaseInfo.ElementAtOrDefault(0);
if (string.IsNullOrEmpty(connInfo.ConnectionDetails.DatabaseName))
{
return null;
}
ReliableDataSourceConnection connection;
connInfo.TryGetConnection("Default", out connection);
IDataSource dataSource = connection.GetUnderlyingConnection();
DataSourceObjectMetadata objectMetadata =
MetadataFactory.CreateClusterMetadata(connInfo.ConnectionDetails.ServerName);
return null;
List<DataSourceObjectMetadata> metadata = dataSource.GetChildObjects(objectMetadata, true).ToList();
var databaseMetadata = metadata.Where(o => o.Name == connInfo.ConnectionDetails.DatabaseName);
List<DatabaseInfo> databaseInfo = MetadataFactory.ConvertToDatabaseInfo(databaseMetadata);
return databaseInfo.ElementAtOrDefault(0);
}
}
}

View File

@@ -59,6 +59,8 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
private IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Map from script URIs to ConnectionInfo objects
/// This is internal for testing access only
@@ -397,7 +399,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
DataSourceObjectMetadata clusterMetadata = MetadataFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
DiagnosticsInfo clusterDiagnostics = dataSource.GetDiagnostics(clusterMetadata);
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerinfoFormat(DataSourceType.Kusto, clusterDiagnostics);
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerInfoFormat(DataSourceType.Kusto, clusterDiagnostics);
response.ServerInfo = new ServerInfo
{
@@ -789,10 +791,11 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
}
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
IConnectedBindingQueue connectedBindingQueue)
IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
{
ServiceHost = serviceHost;
_dataSourceConnectionFactory = dataSourceConnectionFactory;
_dataSourceFactory = dataSourceFactory;
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
LockedDatabaseManager.ConnectionService = this;
@@ -1411,7 +1414,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
string connectionString = BuildConnectionString(connInfo.ConnectionDetails);
// TODOKusto: Pass in type of DataSource needed to make this generic. Hard coded to Kusto right now.
return DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
return _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
}
catch (Exception ex)
{

View File

@@ -17,6 +17,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
[Export(typeof(IDataSourceConnectionFactory))]
public class DataSourceConnectionFactory : IDataSourceConnectionFactory
{
private readonly IDataSourceFactory _dataSourceFactory;
[ImportingConstructor]
public DataSourceConnectionFactory(IDataSourceFactory dataSourceFactory)
{
_dataSourceFactory = dataSourceFactory;
}
/// <summary>
/// Creates a new SqlConnection object
/// </summary>
@@ -24,7 +32,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
RetryPolicy connectionRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
RetryPolicy commandRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
return new ReliableDataSourceConnection(connectionString, connectionRetryPolicy, commandRetryPolicy, azureAccountToken);
return new ReliableDataSourceConnection(connectionString, connectionRetryPolicy, commandRetryPolicy, azureAccountToken, _dataSourceFactory);
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.Kusto.ServiceLayer.DataSource.DataSourceIntellisense;
@@ -9,9 +10,10 @@ using Microsoft.Kusto.ServiceLayer.LanguageServices.Completion;
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
public class DataSourceFactory
[Export(typeof(IDataSourceFactory))]
public class DataSourceFactory : IDataSourceFactory
{
public static IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken)
public IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken)
{
ValidationUtils.IsArgumentNotNullOrWhiteSpace(connectionString, nameof(connectionString));
ValidationUtils.IsArgumentNotNullOrWhiteSpace(azureAccountToken, nameof(azureAccountToken));
@@ -57,15 +59,16 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource
}
}
public static ReliableConnectionHelper.ServerInfo ConvertToServerinfoFormat(DataSourceType dataSourceType, DiagnosticsInfo clusterDiagnostics)
public static ReliableConnectionHelper.ServerInfo ConvertToServerInfoFormat(DataSourceType dataSourceType, DiagnosticsInfo clusterDiagnostics)
{
switch (dataSourceType)
{
case DataSourceType.Kusto:
{
ReliableConnectionHelper.ServerInfo serverInfo = new ReliableConnectionHelper.ServerInfo();
serverInfo.Options = new Dictionary<string, object>(clusterDiagnostics.Options);
return serverInfo;
return new ReliableConnectionHelper.ServerInfo
{
Options = new Dictionary<string, object>(clusterDiagnostics.Options)
};
}
default:

View File

@@ -0,0 +1,7 @@
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
public interface IDataSourceFactory
{
IDataSource Create(DataSourceType dataSourceType, string connectionString, string azureAccountToken);
}
}

View File

@@ -43,6 +43,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
private readonly string _connectionString;
private readonly string _azureAccountToken;
private readonly IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Initializes a new instance of the ReliableKustoClient class with a given connection string
@@ -52,11 +53,15 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// <param name="connectionString">The connection string used to open the SQL Azure database.</param>
/// <param name="connectionRetryPolicy">The retry policy defining whether to retry a request if a connection fails to be established.</param>
/// <param name="commandRetryPolicy">The retry policy defining whether to retry a request if a command fails to be executed.</param>
public ReliableDataSourceConnection(string connectionString, RetryPolicy connectionRetryPolicy, RetryPolicy commandRetryPolicy, string azureAccountToken)
/// <param name="azureAccountToken"></param>
/// <param name="dataSourceFactory"></param>
public ReliableDataSourceConnection(string connectionString, RetryPolicy connectionRetryPolicy,
RetryPolicy commandRetryPolicy, string azureAccountToken, IDataSourceFactory dataSourceFactory)
{
_connectionString = connectionString;
_azureAccountToken = azureAccountToken;
_dataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccountToken);
_dataSourceFactory = dataSourceFactory;
_dataSource = dataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccountToken);
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
@@ -112,42 +117,45 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Gets the policy which decides whether to retry a connection request, based on how many
/// times the request has been made and the reason for the last failure.
/// <summary>
/// Gets the policy which decides whether to retry a connection request, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
// ReSharper disable once UnusedMember.Global
public RetryPolicy ConnectionRetryPolicy
{
get { return _connectionRetryPolicy; }
}
{
get { return _connectionRetryPolicy; }
}
/// <summary>
/// Gets the policy which decides whether to retry a command, based on how many
/// times the request has been made and the reason for the last failure.
/// <summary>
/// Gets the policy which decides whether to retry a command, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
public RetryPolicy CommandRetryPolicy
{
get { return _commandRetryPolicy; }
set
{
Validate.IsNotNull(nameof(value), value);
// ReSharper disable once UnusedMember.Global
public RetryPolicy CommandRetryPolicy
{
get { return _commandRetryPolicy; }
set
{
Validate.IsNotNull(nameof(value), value);
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
_commandRetryPolicy = value;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
}
_commandRetryPolicy = value;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
}
/// <summary>
/// Gets the server name from the underlying connection.
/// </summary>
public string ClusterName
{
get { return _dataSource.ClusterName; }
/// <summary>
/// Gets the server name from the underlying connection.
/// </summary>
// ReSharper disable once UnusedMember.Global
public string ClusterName
{
get { return _dataSource.ClusterName; }
}
/// <summary>
@@ -182,7 +190,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
_connectionRetryPolicy.ExecuteAction(() =>
{
_dataSource = DataSourceFactory.Create(DataSourceType.Kusto, _connectionString, _azureAccountToken);
_dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, _connectionString, _azureAccountToken);
});
}
}
@@ -219,14 +227,15 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
public void Close()
{
}
/// <summary>
/// Gets the time to wait while trying to establish a connection before terminating
/// the attempt and generating an error.
/// </summary>
/// <summary>
/// Gets the time to wait while trying to establish a connection before terminating
/// the attempt and generating an error.
/// </summary>
// ReSharper disable once UnusedMember.Global
public int ConnectionTimeout
{
get { return 30; }
{
get { return 30; }
}
/// <summary>
@@ -237,14 +246,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
{
get { return _dataSource.DatabaseName; }
}
private void VerifyConnectionOpen(ReliableDataSourceConnection conn)
{
if(conn.GetUnderlyingConnection() == null)
{
conn.Open();
}
}
}
}

View File

@@ -71,6 +71,7 @@ namespace Microsoft.Kusto.ServiceLayer
var scripter = serviceProvider.GetService<IScripter>();
var dataSourceConnectionFactory = serviceProvider.GetService<IDataSourceConnectionFactory>();
var connectedBindingQueue = serviceProvider.GetService<IConnectedBindingQueue>();
var dataSourceFactory = serviceProvider.GetService<IDataSourceFactory>();
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
@@ -78,10 +79,10 @@ namespace Microsoft.Kusto.ServiceLayer
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(WorkspaceService<SqlToolsSettings>.Instance);
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue);
LanguageService.Instance.InitializeService(serviceHost, connectedBindingQueue, dataSourceFactory);
serviceProvider.RegisterSingleService(LanguageService.Instance);
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue);
ConnectionService.Instance.InitializeService(serviceHost, dataSourceConnectionFactory, connectedBindingQueue, dataSourceFactory);
serviceProvider.RegisterSingleService(ConnectionService.Instance);
CredentialService.Instance.InitializeService(serviceHost);
@@ -90,10 +91,10 @@ namespace Microsoft.Kusto.ServiceLayer
QueryExecutionService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(QueryExecutionService.Instance);
ScriptingService.Instance.InitializeService(serviceHost, scripter);
ScriptingService.Instance.InitializeService(serviceHost, scripter, dataSourceFactory);
serviceProvider.RegisterSingleService(ScriptingService.Instance);
AdminService.Instance.InitializeService(serviceHost);
AdminService.Instance.InitializeService(serviceHost, ConnectionService.Instance);
serviceProvider.RegisterSingleService(AdminService.Instance);
MetadataService.Instance.InitializeService(serviceHost);

View File

@@ -24,6 +24,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
{
internal const int DefaultBindingTimeout = 500;
private readonly ISqlConnectionOpener _connectionOpener;
private readonly IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Gets the current settings
@@ -34,9 +35,10 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
}
[ImportingConstructor]
public ConnectedBindingQueue(ISqlConnectionOpener sqlConnectionOpener)
public ConnectedBindingQueue(ISqlConnectionOpener sqlConnectionOpener, IDataSourceFactory dataSourceFactory)
{
_connectionOpener = sqlConnectionOpener;
_dataSourceFactory = dataSourceFactory;
}
/// <summary>
@@ -160,7 +162,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
bindingContext.ServerConnection = _connectionOpener.OpenServerConnection(connInfo, featureName);
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
bindingContext.DataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
bindingContext.DataSource = _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
if (needMetadata)
{

View File

@@ -209,7 +209,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
/// <param name="context"></param>
/// <param name="dataSourceFactory"></param>
/// <param name="connectedBindingQueue"></param>
public void InitializeService(ServiceHost serviceHost, IConnectedBindingQueue connectedBindingQueue)
public void InitializeService(ServiceHost serviceHost, IConnectedBindingQueue connectedBindingQueue, IDataSourceFactory dataSourceFactory)
{
_bindingQueue = connectedBindingQueue;
// Register the requests that this service will handle
@@ -854,7 +854,7 @@ namespace Microsoft.Kusto.ServiceLayer.LanguageServices
if (scriptParseInfo == null)
{
var scriptDocInfo = ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile);
resultCompletionItems = resultCompletionItems = DataSourceFactory.GetDefaultAutoComplete(DataSourceType.Kusto, scriptDocInfo, textDocumentPosition.Position); //TODO_KUSTO: DataSourceFactory.GetDefaultAutoComplete 1st param should get the datasource type generically instead of hard coded DataSourceType.Kusto
resultCompletionItems = DataSourceFactory.GetDefaultAutoComplete(DataSourceType.Kusto, scriptDocInfo, textDocumentPosition.Position); //TODO_KUSTO: DataSourceFactory.GetDefaultAutoComplete 1st param should get the datasource type generically instead of hard coded DataSourceType.Kusto
return resultCompletionItems;
}

View File

@@ -11,11 +11,7 @@ using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using System.Collections.Specialized;
using System.Text;
using System.Globalization;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using System.Diagnostics;
@@ -28,19 +24,10 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
{
private readonly IScripter _scripter;
private static readonly Dictionary<string, SqlServerVersion> scriptCompatibilityMap = LoadScriptCompatibilityMap();
/// <summary>
/// Left delimiter for an named object
/// </summary>
public const char LeftDelimiter = '[';
/// <summary>
/// right delimiter for a named object
/// </summary>
public const char RightDelimiter = ']';
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken, IScripter scripter) : base(parameters)
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken, IScripter scripter, IDataSourceFactory dataSourceFactory) : base(parameters, dataSourceFactory)
{
DataSource = DataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString,
DataSource = _dataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString,
azureAccountToken);
_scripter = scripter;
}
@@ -177,200 +164,6 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
}
/// <summary>
/// Generate a schema qualified name (e.g. [schema].[objectName]) for an object if the option for SchemaQualify is true
/// </summary>
/// <param name="schema">The schema name. May be null or empty in which case it will be ignored</param>
/// <param name="objectName">The object name.</param>
/// <param name="schemaQualify">Whether to schema qualify the object or not</param>
/// <returns>The object name, quoted as appropriate and schema-qualified if the option is set</returns>
private static string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
{
var qualifiedName = new StringBuilder();
if (schemaQualify && !String.IsNullOrEmpty(schema))
{
// schema.name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}.{1}", GetDelimitedString(schema), GetDelimitedString(objectName));
}
else
{
// name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}", GetDelimitedString(objectName));
}
return qualifiedName.ToString();
}
/// <summary>
/// getting delimited string
/// </summary>
/// <param name="str">string</param>
/// <returns>string</returns>
static private string GetDelimitedString(string str)
{
if (string.IsNullOrEmpty(str))
{
return String.Empty;
}
else
{
StringBuilder qualifiedName = new StringBuilder();
qualifiedName.AppendFormat("{0}{1}{2}",
LeftDelimiter,
QuoteObjectName(str),
RightDelimiter);
return qualifiedName.ToString();
}
}
/// <summary>
/// turn a smo datatype object into a type that can be inserted into tsql, e.g. nvarchar(20)
/// </summary>
/// <param name="type"></param>
/// <param name="options"></param>
/// <returns></returns>
internal static string GetDatatype(DataType type, ScriptingOptions options)
{
// string we'll return.
string rv = string.Empty;
string dataType = type.Name;
switch (type.SqlDataType)
{
// char, nchar, nchar, nvarchar, varbinary, nvarbinary are all displayed as type(length)
// length of -1 is taken to be type(max). max isn't localizable.
case SqlDataType.Char:
case SqlDataType.NChar:
case SqlDataType.VarChar:
case SqlDataType.NVarChar:
case SqlDataType.Binary:
case SqlDataType.VarBinary:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.MaximumLength);
break;
case SqlDataType.VarCharMax:
case SqlDataType.NVarCharMax:
case SqlDataType.VarBinaryMax:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}(max)",
dataType);
break;
// numeric and decimal are displayed as type precision,scale
case SqlDataType.Numeric:
case SqlDataType.Decimal:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1},{2})",
dataType,
type.NumericPrecision,
type.NumericScale);
break;
//time, datetimeoffset and datetime2 are displayed as type scale
case SqlDataType.Time:
case SqlDataType.DateTimeOffset:
case SqlDataType.DateTime2:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.NumericScale);
break;
// anything else is just type.
case SqlDataType.Xml:
if (type.Schema != null && type.Schema.Length > 0 && dataType != null && dataType.Length > 0)
{
rv = String.Format(CultureInfo.InvariantCulture
, "xml ({0}{2}{1}.{0}{3}{1})"
, LeftDelimiter
, RightDelimiter
, QuoteObjectName(type.Schema)
, QuoteObjectName(dataType));
}
else
{
rv = "xml";
}
break;
case SqlDataType.UserDefinedDataType:
case SqlDataType.UserDefinedTableType:
case SqlDataType.UserDefinedType:
//User defined types may be in a non-DBO schema so append it if necessary
rv = GenerateSchemaQualifiedName(type.Schema, dataType, options.SchemaQualify);
break;
default:
rv = dataType;
break;
}
return rv;
}
/// <summary>
/// Double quotes certain characters in object name
/// </summary>
/// <param name="sqlObject"></param>
public static string QuoteObjectName(string sqlObject)
{
int len = sqlObject.Length;
StringBuilder result = new StringBuilder(sqlObject.Length);
for (int i = 0; i < len; i++)
{
if (sqlObject[i] == ']')
{
result.Append(']');
}
result.Append(sqlObject[i]);
}
return result.ToString();
}
private static void WriteUseDatabase(Database parentObject, StringBuilder stringBuilder , ScriptingOptions options)
{
if (options.IncludeDatabaseContext)
{
string useDb = string.Format(CultureInfo.InvariantCulture, "USE {0}", CommonConstants.DefaultBatchSeperator);
if (!options.NoCommandTerminator)
{
stringBuilder.Append(useDb);
}
else
{
stringBuilder.Append(useDb);
stringBuilder.Append(Environment.NewLine);
}
}
}
private string GetScript(ScriptingOptions options, StringCollection stringCollection)
{
StringBuilder sb = new StringBuilder();
foreach (var item in stringCollection)
{
sb.Append(item);
if (options != null && !options.NoCommandTerminator)
{
//Ensure the batch separator is always on a new line (to avoid syntax errors)
//but don't write an extra if we already have one as this can affect definitions
//of objects such as Stored Procedures (see TFS#9125366)
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}{2}",
item.EndsWith(Environment.NewLine) ? string.Empty : Environment.NewLine,
CommonConstants.DefaultBatchSeperator,
Environment.NewLine);
}
else
{
sb.AppendFormat(CultureInfo.InvariantCulture, Environment.NewLine);
}
}
return sb.ToString();
}
private UrnCollection CreateUrns(IDataSource dataSource)
{
IEnumerable<ScriptingObject> selectedObjects = new List<ScriptingObject>(this.Parameters.ScriptingObjects);
@@ -533,27 +326,5 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
scriptingOptions.AnsiPadding = true;
}
}
private void ScripterScriptingError(object sender, ScriptingErrorEventArgs e)
{
this.CancellationToken.ThrowIfCancellationRequested();
Logger.Write(
TraceEventType.Verbose,
string.Format(
"Sending scripting error progress event, Urn={0}, OperationId={1}, Completed={2}, Error={3}",
e.Current,
this.OperationId,
false,
e?.InnerException?.ToString() ?? "null"));
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
{
ScriptingObject = e.Current?.ToScriptingObject(),
Status = "Failed",
ErrorMessage = e?.InnerException?.Message,
ErrorDetails = e?.InnerException?.ToString(),
});
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
private string azureAccessToken;
public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken) : base(parameters)
public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken, IDataSourceFactory dataSourceFactory) : base(parameters, dataSourceFactory)
{
this.azureAccessToken = azureAccessToken;
}

View File

@@ -36,6 +36,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
private bool disposed;
private IScripter _scripter;
private IDataSourceFactory _dataSourceFactory;
/// <summary>
/// Internal for testing purposes only
@@ -66,9 +67,10 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// </summary>
/// <param name="serviceHost"></param>
/// <param name="context"></param>
public void InitializeService(ServiceHost serviceHost, IScripter scripter)
public void InitializeService(ServiceHost serviceHost, IScripter scripter, IDataSourceFactory dataSourceFactory)
{
_scripter = scripter;
_dataSourceFactory = dataSourceFactory;
serviceHost.SetRequestHandler(ScriptingRequest.Type, this.HandleScriptExecuteRequest);
serviceHost.SetRequestHandler(ScriptingCancelRequest.Type, this.HandleScriptCancelRequest);
serviceHost.SetRequestHandler(ScriptingListObjectsRequest.Type, this.HandleListObjectsRequest);
@@ -131,11 +133,11 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
if (!ShouldCreateScriptAsOperation(parameters))
{
operation = new ScriptingScriptOperation(parameters, accessToken);
operation = new ScriptingScriptOperation(parameters, accessToken, _dataSourceFactory);
}
else
{
operation = new ScriptAsScriptingOperation(parameters, accessToken, _scripter);
operation = new ScriptAsScriptingOperation(parameters, accessToken, _scripter, _dataSourceFactory);
}
operation.PlanNotification += (sender, e) => requestContext.SendEvent(ScriptingPlanNotificationEvent.Type, e).Wait();

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Management.Common;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.SqlTools.Utility;
@@ -22,10 +20,12 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
/// </summary>
public abstract class SmoScriptingOperation : ScriptingOperation
{
protected readonly IDataSourceFactory _dataSourceFactory;
private bool _disposed;
public SmoScriptingOperation(ScriptingParams parameters)
protected SmoScriptingOperation(ScriptingParams parameters, IDataSourceFactory dataSourceFactory)
{
_dataSourceFactory = dataSourceFactory;
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
@@ -77,7 +77,7 @@ namespace Microsoft.Kusto.ServiceLayer.Scripting
{
string serverName = string.Empty;
using(var dataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccessToken))
using(var dataSource = _dataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccessToken))
{
serverName = dataSource.ClusterName;
}

View File

@@ -98,14 +98,6 @@ namespace Microsoft.Kusto.ServiceLayer.Workspace.Contracts
#region Constructors
/// <summary>
/// Add a default constructor for testing
/// </summary>
public ScriptFile()
{
ClientUri = "test.sql";
}
/// <summary>
/// Creates a new ScriptFile instance by reading file contents from
/// the given TextReader.

View File

@@ -33,6 +33,7 @@
<ProjectReference Include="../Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
<ProjectReference Include="../Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
<ProjectReference Include="../Microsoft.SqlTools.ManagedBatchParser/Microsoft.SqlTools.ManagedBatchParser.csproj" />
<ProjectReference Include="../Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgMicrosoft_SqlServer_DacFx)\lib\netstandard2.0\Microsoft.Data.Tools.Schema.SqlTasks.targets">
@@ -48,20 +49,10 @@
<!-- this target enables dependency files to be copied as part of the output of ProjectReference.
https://github.com/dotnet/sdk/issues/1675
-->
<Target Name="AddRuntimeDependenciesToContent"
Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'"
BeforeTargets="GetCopyToOutputDirectoryItems"
DependsOnTargets="GenerateBuildDependencyFile;
GenerateBuildRuntimeConfigurationFiles">
<Target Name="AddRuntimeDependenciesToContent" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;&#xD;&#xA; GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)"
Condition="'$(GenerateDependencyFile)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)"
Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectRuntimeConfigFileName)" />
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" Condition="'$(GenerateDependencyFile)' == 'true'" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" />
</ItemGroup>
</Target>
</Project>