Merge branch 'dev' into feature/queryExecutionV1

Also adding a fancy new mocked out reader for mocking db calls.
This commit is contained in:
Benjamin Russell
2016-08-01 14:27:57 -07:00
86 changed files with 475 additions and 181 deletions

View File

@@ -11,9 +11,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
global.json = global.json
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ServiceHost", "src\ServiceHost\ServiceHost.xproj", "{0D61DC2B-DA66-441D-B9D0-F76C98F780F9}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.SqlTools.ServiceLayer", "src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.xproj", "{0D61DC2B-DA66-441D-B9D0-F76C98F780F9}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ServiceHost.Test", "test\ServiceHost.Test\ServiceHost.Test.xproj", "{2D771D16-9D85-4053-9F79-E2034737DEEF}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.SqlTools.ServiceLayer.Test", "test\Microsoft.SqlTools.ServiceLayer.Test\Microsoft.SqlTools.ServiceLayer.Test.xproj", "{2D771D16-9D85-4053-9F79-E2034737DEEF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -5,14 +5,14 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices;
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
{
@@ -56,12 +56,17 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
/// </summary>
private ISqlConnectionFactory connectionFactory;
/// <summary>
/// The current connection id that was previously used
/// </summary>
private int maxConnectionId = 0;
/// <summary>
/// Active connections lazy dictionary instance
/// </summary>
private Lazy<Dictionary<Guid, ISqlConnection>> activeConnections
= new Lazy<Dictionary<Guid, ISqlConnection>>(()
=> new Dictionary<Guid, ISqlConnection>());
private Lazy<Dictionary<int, ISqlConnection>> activeConnections
= new Lazy<Dictionary<int, ISqlConnection>>(()
=> new Dictionary<int, ISqlConnection>());
/// <summary>
/// Callback for onconnection handler
@@ -77,7 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
/// <summary>
/// Gets the active connection map
/// </summary>
public Dictionary<Guid, ISqlConnection> ActiveConnections
public Dictionary<int, ISqlConnection> ActiveConnections
{
get
{
@@ -117,56 +122,40 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
/// Open a connection with the specified connection details
/// </summary>
/// <param name="connectionDetails"></param>
public async Task<ConnectionResult> Connect(ConnectionDetails connectionDetails)
public ConnectionResult Connect(ConnectionDetails connectionDetails)
{
// build the connection string from the input parameters
string connectionString = BuildConnectionString(connectionDetails);
// create a sql connection instance
ISqlConnection connection = ConnectionFactory.CreateSqlConnection(connectionString);
ISqlConnection connection = this.ConnectionFactory.CreateSqlConnection(connectionString);
// open the database
await connection.OpenAsync();
connection.Open();
// map the connection id to the connection object for future lookups
Guid connectionId = Guid.NewGuid();
ActiveConnections.Add(connectionId, connection);
this.ActiveConnections.Add(++maxConnectionId, connection);
// invoke callback notifications
var onConnectionCallbackTasks = onConnectionActivities.Select(t => t(connection));
await Task.WhenAll(onConnectionCallbackTasks);
// TODO: Evaulate if we want to avoid waiting here. We'll need error handling on the other side if we don't wait
foreach (var activity in this.onConnectionActivities)
{
activity(connection);
}
// return the connection result
return new ConnectionResult
return new ConnectionResult()
{
ConnectionId = connectionId
ConnectionId = maxConnectionId
};
}
/// <summary>
/// Closes an active connection and removes it from the active connections list
/// </summary>
/// <param name="connectionId">ID of the connection to close</param>
public void Disconnect(Guid connectionId)
{
if (!ActiveConnections.ContainsKey(connectionId))
{
// TODO: Should this possibly be a throw condition?
return;
}
ActiveConnections[connectionId].Close();
ActiveConnections.Remove(connectionId);
}
public void Initialize(ServiceHost serviceHost)
public void InitializeService(ServiceHost serviceHost)
{
// Register request and event handlers with the Service Host
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
// Register the shutdown handler
serviceHost.RegisterShutdownTask(HandleShutdownRequest);
// Register the configuration update handler
WorkspaceService<SqlToolsSettings>.Instance.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
}
/// <summary>
@@ -178,15 +167,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
onConnectionActivities.Add(activity);
}
public ISqlConnection GetConnection(Guid connectionId)
{
if (!ActiveConnections.ContainsKey(connectionId))
{
throw new ArgumentException("Connection with provided ID could not be found");
}
return ActiveConnections[connectionId];
}
#endregion
#region Request Handlers
@@ -203,24 +183,28 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
{
Logger.Write(LogLevel.Verbose, "HandleConnectRequest");
// open connection base on request details
ConnectionResult result = await Connect(connectionDetails);
await requestContext.SendResult(result);
try
{
// open connection base on request details
ConnectionResult result = ConnectionService.Instance.Connect(connectionDetails);
await requestContext.SendResult(result);
}
catch(Exception ex)
{
await requestContext.SendError(ex.Message);
}
}
/// <summary>
/// Handles shutdown event by closing out any active connections
/// </summary>
protected async Task HandleShutdownRequest(object shutdownObj, RequestContext<object> shutdownContext)
{
// Go through all the existing connections and close them out
foreach (ISqlConnection conn in ActiveConnections.Values)
{
conn.Close();
}
#endregion
await Task.FromResult(0);
#region Handlers for Events from Other Services
public Task HandleDidChangeConfigurationNotification(
SqlToolsSettings newSettings,
SqlToolsSettings oldSettings,
EventContext eventContext)
{
return Task.FromResult(true);
}
#endregion

View File

@@ -3,10 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices
{
/// <summary>
/// Message format for the initial connection request
@@ -43,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts
/// <summary>
/// Gets or sets the connection id
/// </summary>
public Guid ConnectionId { get; set; }
public int ConnectionId { get; set; }
/// <summary>
/// Gets or sets any connection error messages

View File

@@ -45,7 +45,7 @@ namespace Microsoft.SqlTools.ServiceLayer
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
AutoCompleteService.Instance.InitializeService(serviceHost);
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
ConnectionService.Instance.Initialize(serviceHost);
ConnectionService.Instance.InitializeService(serviceHost);
serviceHost.Initialize();
serviceHost.WaitForExit();

View File

@@ -41,4 +41,4 @@ using System.Runtime.InteropServices;
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceHost.Test")]
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.Test")]

View File

@@ -1,5 +1,5 @@
{
"name": "Microsoft.SqlTools.ServiceHost",
"name": "Microsoft.SqlTools.ServiceLayer",
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",

View File

@@ -8,7 +8,7 @@ using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
using Microsoft.SqlTools.Test.Utility;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer
namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
{
/// <summary>
/// Tests for the ServiceHost Language Service tests
@@ -115,7 +115,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer
var connectionService = TestObjects.GetTestConnectionService();
var connectionResult = connectionService.Connect(TestObjects.GetTestConnectionDetails());
var sqlConnection = connectionService.ActiveConnections[connectionResult.ConnectionId];
autocompleteService.UpdateAutoCompleteCache(sqlConnection);
autocompleteService.UpdateAutoCompleteCache(sqlConnection).Wait();
}
#endregion

View File

@@ -0,0 +1,419 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//#define USE_LIVE_CONNECTION
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices;
using Microsoft.SqlTools.ServiceLayer.ConnectionServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
namespace Microsoft.SqlTools.Test.Utility
{
/// <summary>
/// Tests for the ServiceHost Connection Service tests
/// </summary>
public class TestObjects
{
/// <summary>
/// Creates a test connection service
/// </summary>
public static ConnectionService GetTestConnectionService()
{
#if !USE_LIVE_CONNECTION
// use mock database connection
return new ConnectionService(new TestSqlConnectionFactory());
#else
// connect to a real server instance
return ConnectionService.Instance;
#endif
}
/// <summary>
/// Creates a test connection details object
/// </summary>
public static ConnectionDetails GetTestConnectionDetails()
{
return new ConnectionDetails()
{
UserName = "sa",
Password = "Yukon900",
DatabaseName = "AdventureWorks2016CTP3_2",
ServerName = "sqltools11"
};
}
/// <summary>
/// Create a test language service instance
/// </summary>
/// <returns></returns>
public static LanguageService GetTestLanguageService()
{
return new LanguageService();
}
/// <summary>
/// Creates a test autocomplete service instance
/// </summary>
public static AutoCompleteService GetAutoCompleteService()
{
return AutoCompleteService.Instance;
}
/// <summary>
/// Creates a test sql connection factory instance
/// </summary>
public static ISqlConnectionFactory GetTestSqlConnectionFactory()
{
#if !USE_LIVE_CONNECTION
// use mock database connection
return new TestSqlConnectionFactory();
#else
// connect to a real server instance
return ConnectionService.Instance.ConnectionFactory;
#endif
}
}
public class TestSqlReader : IDataReader
{
#region Test Specific Implementations
internal string SqlCommandText { get; set; }
private const string tableNameTestCommand = "SELECT name FROM sys.tables";
private List<Dictionary<string, string>> tableNamesTest = new List<Dictionary<string, string>>
{
new Dictionary<string, string> { {"name", "table1"} },
new Dictionary<string, string> { {"name", "table2"} }
};
private IEnumerator<Dictionary<string, string>> tableEnumerator;
#endregion
public bool GetBoolean(int i)
{
throw new NotImplementedException();
}
public byte GetByte(int i)
{
throw new NotImplementedException();
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
throw new NotImplementedException();
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
throw new NotImplementedException();
}
public decimal GetDecimal(int i)
{
throw new NotImplementedException();
}
public double GetDouble(int i)
{
throw new NotImplementedException();
}
public Type GetFieldType(int i)
{
throw new NotImplementedException();
}
public float GetFloat(int i)
{
throw new NotImplementedException();
}
public Guid GetGuid(int i)
{
throw new NotImplementedException();
}
public short GetInt16(int i)
{
throw new NotImplementedException();
}
public int GetInt32(int i)
{
throw new NotImplementedException();
}
public long GetInt64(int i)
{
throw new NotImplementedException();
}
public string GetName(int i)
{
throw new NotImplementedException();
}
public int GetOrdinal(string name)
{
throw new NotImplementedException();
}
public string GetString(int i)
{
throw new NotImplementedException();
}
public object GetValue(int i)
{
throw new NotImplementedException();
}
public int GetValues(object[] values)
{
throw new NotImplementedException();
}
public bool IsDBNull(int i)
{
throw new NotImplementedException();
}
public int FieldCount { get; }
object IDataRecord.this[string name]
{
get { return tableEnumerator.Current[name]; }
}
object IDataRecord.this[int i]
{
get { return tableEnumerator.Current[tableEnumerator.Current.Keys.ToArray()[i]]; }
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Close()
{
throw new NotImplementedException();
}
public DataTable GetSchemaTable()
{
throw new NotImplementedException();
}
public bool NextResult()
{
throw new NotImplementedException();
}
public bool Read()
{
if (tableEnumerator == null)
{
switch (SqlCommandText)
{
case tableNameTestCommand:
tableEnumerator = ((IEnumerable<Dictionary<string, string>>)tableNamesTest).GetEnumerator();
break;
default:
throw new NotImplementedException();
}
}
return tableEnumerator.MoveNext();
}
public int Depth { get; }
public bool IsClosed { get; }
public int RecordsAffected { get; }
}
/// <summary>
/// Test mock class for IDbCommand
/// </summary>
public class TestSqlCommand : IDbCommand
{
public string CommandText { get; set; }
public int CommandTimeout { get; set; }
public CommandType CommandType { get; set; }
public IDbConnection Connection { get; set; }
public IDataParameterCollection Parameters
{
get
{
throw new NotImplementedException();
}
}
public IDbTransaction Transaction { get; set; }
public UpdateRowSource UpdatedRowSource { get; set; }
public void Cancel()
{
throw new NotImplementedException();
}
public IDbDataParameter CreateParameter()
{
throw new NotImplementedException();
}
public void Dispose()
{
}
public int ExecuteNonQuery()
{
throw new NotImplementedException();
}
public IDataReader ExecuteReader()
{
return new TestSqlReader
{
SqlCommandText = CommandText
};
}
public IDataReader ExecuteReader(CommandBehavior behavior)
{
throw new NotImplementedException();
}
public object ExecuteScalar()
{
throw new NotImplementedException();
}
public void Prepare()
{
throw new NotImplementedException();
}
}
/// <summary>
/// Test mock class for SqlConnection wrapper
/// </summary>
public class TestSqlConnection : ISqlConnection
{
public TestSqlConnection(string connectionString)
{
}
public void Dispose()
{
throw new System.NotImplementedException();
}
public IDbTransaction BeginTransaction()
{
throw new System.NotImplementedException();
}
public IDbTransaction BeginTransaction(IsolationLevel il)
{
throw new System.NotImplementedException();
}
public void Close()
{
throw new System.NotImplementedException();
}
public IDbCommand CreateCommand()
{
return new TestSqlCommand {Connection = this};
}
public void Open()
{
// No Op.
}
public string ConnectionString { get; set; }
public int ConnectionTimeout { get; }
public string Database { get; }
public ConnectionState State { get; }
public void ChangeDatabase(string databaseName)
{
throw new System.NotImplementedException();
}
public string DataSource { get; }
public string ServerVersion { get; }
public void ClearPool()
{
throw new System.NotImplementedException();
}
public async Task OpenAsync()
{
// No Op.
await Task.FromResult(0);
}
public Task OpenAsync(CancellationToken token)
{
throw new System.NotImplementedException();
}
}
/// <summary>
/// Test mock class for SqlConnection factory
/// </summary>
public class TestSqlConnectionFactory : ISqlConnectionFactory
{
public ISqlConnection CreateSqlConnection(string connectionString)
{
return new TestSqlConnection(connectionString);
}
}
}

View File

@@ -1,5 +1,5 @@
{
"name": "Microsoft.SqlTools.ServiceHost.Test",
"name": "Microsoft.SqlTools.ServiceLayer.Test",
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
@@ -11,7 +11,7 @@
"System.Data.SqlClient": "4.1.0",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-192208-24",
"ServiceHost": {
"Microsoft.SqlTools.ServiceLayer": {
"target": "project"
}
},

View File

@@ -1,108 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//#define USE_LIVE_CONNECTION
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
namespace Microsoft.SqlTools.Test.Utility
{
/// <summary>
/// Tests for the ServiceHost Connection Service tests
/// </summary>
public class TestObjects
{
/// <summary>
/// Creates a test connection service
/// </summary>
public static ConnectionService GetTestConnectionService()
{
#if !USE_LIVE_CONNECTION
// use mock database connection
return new ConnectionService(new TestSqlConnectionFactory());
#else
// connect to a real server instance
return ConnectionService.Instance;
#endif
}
/// <summary>
/// Creates a test connection details object
/// </summary>
public static ConnectionDetails GetTestConnectionDetails()
{
return new ConnectionDetails()
{
UserName = "sa",
Password = "Yukon900",
DatabaseName = "AdventureWorks2016CTP3_2",
ServerName = "sqltools11"
};
}
/// <summary>
/// Create a test language service instance
/// </summary>
/// <returns></returns>
public static LanguageService GetTestLanguageService()
{
return new LanguageService();
}
/// <summary>
/// Creates a test autocomplete service instance
/// </summary>
public static AutoCompleteService GetAutoCompleteService()
{
return AutoCompleteService.Instance;
}
/// <summary>
/// Creates a test sql connection factory instance
/// </summary>
public static ISqlConnectionFactory GetTestSqlConnectionFactory()
{
#if !USE_LIVE_CONNECTION
// use mock database connection
return new TestSqlConnectionFactory();
#else
// connect to a real server instance
return ConnectionService.Instance.ConnectionFactory;
#endif
}
}
/// <summary>
/// Test mock class for SqlConnection wrapper
/// </summary>
public class TestSqlConnection : ISqlConnection
{
public void OpenDatabaseConnection(string connectionString)
{
}
public IEnumerable<string> GetServerObjects()
{
return null;
}
}
/// <summary>
/// Test mock class for SqlConnection factory
/// </summary>
public class TestSqlConnectionFactory : ISqlConnectionFactory
{
public ISqlConnection CreateSqlConnection()
{
return new TestSqlConnection();
}
}
}