diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
index 36ed613a..b6f7b17a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
@@ -5,6 +5,8 @@
using System;
using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Common;
@@ -195,11 +197,57 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
return true;
}
+ ///
+ /// List all databases on the server specified
+ ///
+ public ListDatabasesResponse ListDatabases(ListDatabasesParams listDatabasesParams)
+ {
+ // Verify parameters
+ var owner = listDatabasesParams.OwnerUri;
+ if (String.IsNullOrEmpty(owner))
+ {
+ throw new ArgumentException("OwnerUri cannot be null or empty");
+ }
+
+ // Use the existing connection as a base for the search
+ ConnectionInfo info;
+ if (!TryFindConnection(owner, out info))
+ {
+ throw new Exception("Specified OwnerUri \"" + owner + "\" does not have an existing connection");
+ }
+ ConnectionDetails connectionDetails = info.ConnectionDetails.Clone();
+
+ // Connect to master and query sys.databases
+ connectionDetails.DatabaseName = "master";
+ var connection = this.ConnectionFactory.CreateSqlConnection(BuildConnectionString(connectionDetails));
+ connection.Open();
+
+ DbCommand command = connection.CreateCommand();
+ command.CommandText = "SELECT name FROM sys.databases";
+ command.CommandTimeout = 15;
+ command.CommandType = CommandType.Text;
+ var reader = command.ExecuteReader();
+
+ List results = new List();
+ while (reader.Read())
+ {
+ results.Add(reader[0].ToString());
+ }
+
+ connection.Close();
+
+ ListDatabasesResponse response = new ListDatabasesResponse();
+ response.DatabaseNames = results.ToArray();
+
+ return response;
+ }
+
public void InitializeService(IProtocolEndpoint serviceHost)
{
// Register request and event handlers with the Service Host
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
serviceHost.SetRequestHandler(DisconnectRequest.Type, HandleDisconnectRequest);
+ serviceHost.SetRequestHandler(ListDatabasesRequest.Type, HandleListDatabasesRequest);
// Register the configuration update handler
WorkspaceService.Instance.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
@@ -266,6 +314,26 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
}
}
+
+ ///
+ /// Handle requests to list databases on the current server
+ ///
+ protected async Task HandleListDatabasesRequest(
+ ListDatabasesParams listDatabasesParams,
+ RequestContext requestContext)
+ {
+ Logger.Write(LogLevel.Verbose, "ListDatabasesRequest");
+
+ try
+ {
+ ListDatabasesResponse result = ConnectionService.Instance.ListDatabases(listDatabasesParams);
+ await requestContext.SendResult(result);
+ }
+ catch(Exception ex)
+ {
+ await requestContext.SendError(ex.ToString());
+ }
+ }
public Task HandleDidChangeConfigurationNotification(
SqlToolsSettings newSettings,
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetailsExtensions.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetailsExtensions.cs
new file mode 100644
index 00000000..de278dbc
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetailsExtensions.cs
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
+{
+ ///
+ /// Extension methods for the ConnectionDetails contract class
+ ///
+ public static class ConnectionDetailsExtensions
+ {
+ ///
+ /// Create a copy of a connection details object.
+ ///
+ public static ConnectionDetails Clone(this ConnectionDetails details)
+ {
+ return new ConnectionDetails()
+ {
+ ServerName = details.ServerName,
+ DatabaseName = details.DatabaseName,
+ UserName = details.UserName,
+ Password = details.Password
+ };
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesParams.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesParams.cs
new file mode 100644
index 00000000..fa607e75
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesParams.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
+{
+ ///
+ /// Parameters for the List Databases Request.
+ ///
+ public class ListDatabasesParams
+ {
+ ///
+ /// URI of the owner of the connection requesting the list of databases.
+ ///
+ public string OwnerUri { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesRequest.cs
new file mode 100644
index 00000000..01c12a45
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesRequest.cs
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
+{
+ ///
+ /// List databases request mapping entry
+ ///
+ public class ListDatabasesRequest
+ {
+ public static readonly
+ RequestType Type =
+ RequestType.Create("connection/listdatabases");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesResponse.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesResponse.cs
new file mode 100644
index 00000000..68610803
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ListDatabasesResponse.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
+{
+ ///
+ /// Message format for the list databases response
+ ///
+ public class ListDatabasesResponse
+ {
+ ///
+ /// Gets or sets the list of database names.
+ ///
+ public string[] DatabaseNames { get; set; }
+ }
+}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/Connection/ConnectionServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/Connection/ConnectionServiceTests.cs
index 6333bbd6..b6888b0a 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/Connection/ConnectionServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/Connection/ConnectionServiceTests.cs
@@ -4,12 +4,17 @@
//
using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.Test.Utility;
using Moq;
+using Moq.Protected;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
@@ -20,6 +25,32 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
public class ConnectionServiceTests
{
///
+ /// Creates a mock db command that returns a predefined result set
+ ///
+ public static DbCommand CreateTestCommand(Dictionary[][] data)
+ {
+ var commandMock = new Mock { CallBase = true };
+ var commandMockSetup = commandMock.Protected()
+ .Setup("ExecuteDbDataReader", It.IsAny());
+
+ commandMockSetup.Returns(new TestDbDataReader(data));
+
+ return commandMock.Object;
+ }
+
+ ///
+ /// Creates a mock db connection that returns predefined data when queried for a result set
+ ///
+ public DbConnection CreateMockDbConnection(Dictionary[][] data)
+ {
+ var connectionMock = new Mock { CallBase = true };
+ connectionMock.Protected()
+ .Setup("CreateDbCommand")
+ .Returns(CreateTestCommand(data));
+
+ return connectionMock.Object;
+ }
+
/// Verify that we can connect to the default database when no database name is
/// provided as a parameter.
///
@@ -331,6 +362,55 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
Assert.False(disconnectResult);
}
+ ///
+ /// Verifies the the list databases operation lists database names for the server used by a connection.
+ ///
+ [Fact]
+ public void ListDatabasesOnServerForCurrentConnectionReturnsDatabaseNames()
+ {
+ // Result set for the query of database names
+ Dictionary[] data =
+ {
+ new Dictionary { {"name", "master" } },
+ new Dictionary { {"name", "model" } },
+ new Dictionary { {"name", "msdb" } },
+ new Dictionary { {"name", "tempdb" } },
+ new Dictionary { {"name", "mydatabase" } },
+ };
+
+ // Setup mock connection factory to inject query results
+ var mockFactory = new Mock();
+ mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny()))
+ .Returns(CreateMockDbConnection(new[] {data}));
+ var connectionService = new ConnectionService(mockFactory.Object);
+
+ // connect to a database instance
+ string ownerUri = "file://my/sample/file.sql";
+ var connectionResult =
+ connectionService
+ .Connect(new ConnectParams()
+ {
+ OwnerUri = ownerUri,
+ Connection = TestObjects.GetTestConnectionDetails()
+ });
+
+ // verify that a valid connection id was returned
+ Assert.NotEmpty(connectionResult.ConnectionId);
+
+ // list databases for the connection
+ ListDatabasesParams parameters = new ListDatabasesParams();
+ parameters.OwnerUri = ownerUri;
+ var listDatabasesResult = connectionService.ListDatabases(parameters);
+ string[] databaseNames = listDatabasesResult.DatabaseNames;
+
+ Assert.Equal(databaseNames.Length, 5);
+ Assert.Equal(databaseNames[0], "master");
+ Assert.Equal(databaseNames[1], "model");
+ Assert.Equal(databaseNames[2], "msdb");
+ Assert.Equal(databaseNames[3], "tempdb");
+ Assert.Equal(databaseNames[4], "mydatabase");
+ }
+
///
/// Verify that the SQL parser correctly detects errors in text
///