diff --git a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
index f017bf6f..c8ca7258 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
@@ -40,6 +40,7 @@ using Microsoft.SqlTools.ServiceLayer.ModelManagement;
using Microsoft.SqlTools.ServiceLayer.TableDesigner;
using Microsoft.SqlTools.ServiceLayer.AzureBlob;
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
+using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using System.IO;
namespace Microsoft.SqlTools.ServiceLayer
@@ -179,6 +180,9 @@ namespace Microsoft.SqlTools.ServiceLayer
ExecutionPlanService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
+ ObjectManagementService.Instance.InitializeService(serviceHost);
+ serviceProvider.RegisterSingleService(ObjectManagementService.Instance);
+
serviceHost.InitializeRequestHandlers();
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
index bd28ccb4..01ec43f5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
@@ -9613,6 +9613,14 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
+ public static string ErrorConnectionNotFound
+ {
+ get
+ {
+ return Keys.GetString(Keys.ErrorConnectionNotFound);
+ }
+ }
+
public static string ConnectionServiceListDbErrorNotConnected(string uri)
{
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
@@ -13878,6 +13886,8 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string GetUserDefinedObjectsFromModelFailed = "GetUserDefinedObjectsFromModelFailed";
+ public const string ErrorConnectionNotFound = "ErrorConnectionNotFound";
+
private Keys()
{ }
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
index 186cc61b..0945eaa0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
@@ -2415,4 +2415,7 @@ TableDesignerConfirmationText = I have read the summary and understand the poten
SqlProjectModelNotFound(string projectUri) = Could not find SQL model from project: {0}.
UnsupportedModelType(string type) = Unsupported model type: {0}.
-GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from model.
\ No newline at end of file
+GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from model.
+
+#ObjectManagement Service
+ErrorConnectionNotFound = The connection could not be found
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/RenameRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/RenameRequest.cs
new file mode 100644
index 00000000..74c50b2d
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/Contracts/RenameRequest.cs
@@ -0,0 +1,29 @@
+//
+// 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.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
+{
+ public class RenameRequestParams : GeneralRequestDetails
+ {
+ ///
+ /// SFC (SMO) URN identifying the object
+ ///
+ public string ObjectUrn { get; set; }
+ ///
+ /// the new name of the object
+ ///
+ public string NewName { get; set; }
+ ///
+ /// Connection uri
+ ///
+ public string ConnectionUri { get; set; }
+ }
+ public class RenameRequest
+ {
+ public static readonly RequestType Type = RequestType.Create("objectmanagement/rename");
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs
new file mode 100644
index 00000000..0fd44f9d
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs
@@ -0,0 +1,98 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+using System;
+using System.Threading.Tasks;
+using Microsoft.Data.SqlClient;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlTools.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
+{
+ ///
+ /// Main class for ObjectManagement Service functionality
+ ///
+ public class ObjectManagementService
+ {
+ private const string ObjectManagementServiceApplicationName = "azdata-object-management";
+ private static Lazy objectManagementServiceInstance = new Lazy(() => new ObjectManagementService());
+ public static ObjectManagementService Instance => objectManagementServiceInstance.Value;
+ public static ConnectionService connectionService;
+ private IProtocolEndpoint serviceHost;
+ public ObjectManagementService() { }
+
+ ///
+ /// Internal for testing purposes only
+ ///
+ internal static ConnectionService ConnectionServiceInstance
+ {
+ get
+ {
+ connectionService ??= ConnectionService.Instance;
+ return connectionService;
+ }
+ set
+ {
+ connectionService = value;
+ }
+ }
+
+ public void InitializeService(IProtocolEndpoint serviceHost)
+ {
+ this.serviceHost = serviceHost;
+ this.serviceHost.SetRequestHandler(RenameRequest.Type, HandleRenameRequest);
+ }
+
+ ///
+ /// Method to handle the renaming operation
+ ///
+ /// parameters which are needed to execute renaming operation
+ /// Request Context
+ ///
+ internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext requestContext)
+ {
+ Logger.Verbose("Handle Request in HandleProcessRenameEditRequest()");
+ ConnectionInfo connInfo;
+
+ if (connectionService.TryFindConnection(
+ requestParams.ConnectionUri,
+ out connInfo))
+ {
+ using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, ObjectManagementServiceApplicationName))
+ {
+
+ IRenamable renameObject = this.GetRenamable(requestParams, sqlConn);
+
+ renameObject.Rename(requestParams.NewName);
+ }
+ }
+ else
+ {
+ Logger.Error($"The connection {requestParams.ConnectionUri} could not be found.");
+ throw new Exception(SR.ErrorConnectionNotFound);
+ }
+
+ await requestContext.SendResult(true);
+ }
+
+ ///
+ /// Method to get the sql object, which should be renamed
+ ///
+ /// parameters which are required for the rename operation
+ /// the sqlconnection on the server to search for the sqlobject
+ /// the sql object if implements the interface IRenamable, so they can be renamed
+ private IRenamable GetRenamable(RenameRequestParams requestParams, SqlConnection connection)
+ {
+ ServerConnection serverConnection = new ServerConnection(connection);
+ Server server = new Server(serverConnection);
+ SqlSmoObject dbObject = server.GetSmoObject(new Urn(requestParams.ObjectUrn));
+ return (IRenamable)dbObject;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementServiceTests.cs
new file mode 100644
index 00000000..aea0a4d3
--- /dev/null
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementServiceTests.cs
@@ -0,0 +1,149 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+using System;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlTools.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
+using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
+using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
+using Microsoft.SqlTools.ServiceLayer.SqlContext;
+using Microsoft.SqlTools.ServiceLayer.Test.Common;
+using Moq;
+using NUnit.Framework;
+using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
+
+namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
+{
+ public class ObjectManagementServiceTests
+ {
+ private const string TableQuery = @"CREATE TABLE testTable1_RenamingTable (c1 int)";
+ private const string OwnerUri = "testDB";
+ private ObjectManagementService objectManagementService;
+ private SqlTestDb testDb;
+ private Mock> requestContextMock;
+
+ [SetUp]
+ public async Task TestInitialize()
+ {
+ this.testDb = await SqlTestDb.CreateNewAsync(serverType: TestServerType.OnPrem, query: TableQuery, dbNamePrefix: "RenameTest");
+
+ requestContextMock = new Mock>();
+ ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
+
+ TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, OwnerUri, ConnectionType.Default);
+
+ ObjectManagementService.ConnectionServiceInstance = connectionService;
+ this.objectManagementService = new ObjectManagementService();
+ }
+
+ [TearDown]
+ public async Task TearDownTestDatabase()
+ {
+ await SqlTestDb.DropDatabase(testDb.DatabaseName);
+ }
+
+ [Test]
+ public async Task TestRenameTable()
+ {
+ //arrange & act
+ await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
+
+ //assert
+ requestContextMock.Verify(x => x.SendResult(It.Is(r => r == true)));
+
+ Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.tables WHERE name='RenamingTable'");
+ Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed table was not executed");
+ Assert.That(queryRenameObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the renamed table");
+ Assert.That(queryRenameObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(1), "Did not find the table with the new name after the rename operation");
+
+ Query queryOldObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.tables WHERE name='testTable1_RenamingTable'");
+ Assert.That(queryOldObject.HasExecuted, Is.True, "The query to check for the old table was not executed");
+ Assert.That(queryOldObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the old table");
+ Assert.That(queryOldObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(0), "Did find the old table which should have been renamed");
+ }
+
+ [Test]
+ public async Task TestRenameColumn()
+ {
+ //arrange & act
+ await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1']", testDb.DatabaseName)), requestContextMock.Object);
+
+ //assert
+ requestContextMock.Verify(x => x.SendResult(It.Is(r => r == true)));
+
+ Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.columns WHERE name='RenameColumn'");
+ Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed column was not executed");
+ Assert.That(queryRenameObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the renamed column");
+ Assert.That(queryRenameObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(1), "Did not find the column with the new name after the rename operation");
+
+ Query queryOldObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.columns WHERE name='C1'");
+ Assert.That(queryOldObject.HasExecuted, Is.True, "The query to check for the old column was not executed");
+ Assert.That(queryOldObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the old column");
+ Assert.That(queryOldObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(0), "Did find the old column which should have been renamed");
+ }
+
+ [Test]
+ public async Task TestRenameColumnNotExisting()
+ {
+ Assert.That(async () =>
+ {
+ await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1_NOT']", testDb.DatabaseName)), requestContextMock.Object);
+ }, Throws.Exception.TypeOf(), "Did find the column, which should not have existed");
+ }
+
+ [Test]
+ public async Task TestRenameTableNotExisting()
+ {
+ Assert.That(async () =>
+ {
+ await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
+ }, Throws.Exception.TypeOf(), "Did find the table, which should not have existed");
+ }
+
+ [Test]
+ public async Task TestConnectionNotFound()
+ {
+ var testRenameRequestParams = new RenameRequestParams
+ {
+ NewName = "RenamingTable",
+ ConnectionUri = "NOT_EXISTING",
+ ObjectUrn = String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName),
+ };
+ Assert.That(async () =>
+ {
+ await objectManagementService.HandleRenameRequest(testRenameRequestParams, requestContextMock.Object);
+
+ }, Throws.Exception.TypeOf(), "Did find the connection, which should not have existed");
+ }
+
+ private RenameRequestParams InitRequestParams(string newName, string UrnOfObject)
+ {
+ return new RenameRequestParams
+ {
+ NewName = newName,
+ ConnectionUri = OwnerUri,
+ ObjectUrn = UrnOfObject,
+ };
+ }
+
+ private Query ExecuteQuery(string queryText)
+ {
+ TestConnectionResult conResult = LiveConnectionHelper.InitLiveConnectionInfo();
+ ConnectionInfo connInfo = conResult.ConnectionInfo;
+ IFileStreamFactory fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(new ConcurrentDictionary());
+
+ QueryExecutionSettings settings = new QueryExecutionSettings() { IsSqlCmdMode = false };
+ Query query = new Query(queryText, connInfo, settings, fileStreamFactory);
+ query.Execute();
+ query.ExecutionTask.Wait();
+ return query;
+ }
+ }
+}
\ No newline at end of file