diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePostDeploymentScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePostDeploymentScript.cs index 0f416197..f98bfc04 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePostDeploymentScript.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePostDeploymentScript.cs @@ -4,8 +4,8 @@ // using Microsoft.SqlTools.Hosting.Protocol.Contracts; - using Microsoft.SqlTools.ServiceLayer.Utility; + namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts { public class DeletePostDeploymentScriptRequest diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePreDeploymentScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePreDeploymentScript.cs index c9aeb06a..a6ee3998 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePreDeploymentScript.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/DeletePreDeploymentScript.cs @@ -4,8 +4,8 @@ // using Microsoft.SqlTools.Hosting.Protocol.Contracts; - using Microsoft.SqlTools.ServiceLayer.Utility; + namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts { public class DeletePreDeploymentScriptRequest diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePostDeploymentScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePostDeploymentScript.cs new file mode 100644 index 00000000..08dbfe0c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePostDeploymentScript.cs @@ -0,0 +1,15 @@ +// +// 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.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class MovePostDeploymentScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlProjects/movePostDeploymentScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePreDeploymentScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePreDeploymentScript.cs new file mode 100644 index 00000000..275caab1 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/PrePostDeploymentScripts/MovePreDeploymentScript.cs @@ -0,0 +1,15 @@ +// +// 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.ServiceLayer.Utility; +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + public class MovePreDeploymentScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlProjects/movePreDeploymentScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjectScripts/MoveSqlObjectScript.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjectScripts/MoveSqlObjectScript.cs new file mode 100644 index 00000000..c76003fb --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/Contracts/SqlObjectScripts/MoveSqlObjectScript.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#nullable disable + +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts +{ + /// + /// Parameters for moving an item from Path to DestinationPath + /// + public class MoveItemParams : SqlProjectScriptParams + { + /// + /// Destination path of the file or folder, relative to the .sqlproj + /// + public string DestinationPath { get; set; } + } + + public class MoveSqlObjectScriptRequest + { + public static readonly RequestType Type = RequestType.Create("sqlProjects/moveSqlObjectScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs index 9663f92f..b4d8293b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs @@ -50,15 +50,18 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects serviceHost.SetRequestHandler(AddSqlObjectScriptRequest.Type, HandleAddSqlObjectScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(DeleteSqlObjectScriptRequest.Type, HandleDeleteSqlObjectScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(ExcludeSqlObjectScriptRequest.Type, HandleExcludeSqlObjectScriptRequest, isParallelProcessingSupported: false); + serviceHost.SetRequestHandler(MoveSqlObjectScriptRequest.Type, HandleMoveSqlObjectScriptRequest, isParallelProcessingSupported: false); // Pre/Post-deployment script functions serviceHost.SetRequestHandler(AddPreDeploymentScriptRequest.Type, HandleAddPreDeploymentScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(DeletePreDeploymentScriptRequest.Type, HandleDeletePreDeploymentScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(ExcludePreDeploymentScriptRequest.Type, HandleExcludePreDeploymentScriptRequest, isParallelProcessingSupported: false); + serviceHost.SetRequestHandler(MovePreDeploymentScriptRequest.Type, HandleMovePreDeploymentScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(AddPostDeploymentScriptRequest.Type, HandleAddPostDeploymentScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(DeletePostDeploymentScriptRequest.Type, HandleDeletePostDeploymentScriptRequest, isParallelProcessingSupported: false); serviceHost.SetRequestHandler(ExcludePostDeploymentScriptRequest.Type, HandleExcludePostDeploymentScriptRequest, isParallelProcessingSupported: false); + serviceHost.SetRequestHandler(MovePostDeploymentScriptRequest.Type, HandleMovePostDeploymentScriptRequest, isParallelProcessingSupported: false); // Folder functions serviceHost.SetRequestHandler(AddFolderRequest.Type, HandleAddFolderRequest, isParallelProcessingSupported: false); @@ -137,6 +140,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlObjectScripts.Exclude(requestParams.Path!), requestContext); } + internal async Task HandleMoveSqlObjectScriptRequest(MoveItemParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Move(requestParams.Path, requestParams.DestinationPath), requestContext); + } + #endregion #region Pre/Post-deployment script functions @@ -156,6 +164,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).PreDeployScripts.Exclude(requestParams.Path), requestContext); } + internal async Task HandleMovePreDeploymentScriptRequest(MoveItemParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).PreDeployScripts.Move(requestParams.Path, requestParams.DestinationPath), requestContext); + } + internal async Task HandleAddPostDeploymentScriptRequest(SqlProjectScriptParams requestParams, RequestContext requestContext) { await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).PostDeployScripts.Add(new PostDeployScript(requestParams.Path)), requestContext); @@ -171,6 +184,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).PostDeployScripts.Exclude(requestParams.Path), requestContext); } + internal async Task HandleMovePostDeploymentScriptRequest(MoveItemParams requestParams, RequestContext requestContext) + { + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).PostDeployScripts.Move(requestParams.Path, requestParams.DestinationPath), requestContext); + } + #endregion #region Folder functions diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs index eb5d037d..b523f267 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs @@ -101,7 +101,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects } [Test] - public async Task TestSqlObjectScriptAddDeleteExclude() + public async Task TestSqlObjectScriptAddDeleteExcludeMove() { // Setup SqlProjectsService service = new(); @@ -111,9 +111,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects // Validate adding a SQL object script MockRequest requestMock = new(); string scriptRelativePath = "MyTable.sql"; - string scriptFullPath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); - await File.WriteAllTextAsync(scriptFullPath, "CREATE TABLE [MyTable] ([Id] INT)"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to be on disk"); + string scriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); + await File.WriteAllTextAsync(scriptAbsolutePath, "CREATE TABLE [MyTable] ([Id] INT)"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to be on disk"); await service.HandleAddSqlObjectScriptRequest(new SqlProjectScriptParams() { @@ -135,7 +135,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects requestMock.AssertSuccess(nameof(service.HandleExcludeSqlObjectScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].SqlObjectScripts.Count, "SqlObjectScripts count after exclude"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to still exist on disk"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to still exist on disk"); // Re-add to set up for Delete requestMock = new(); @@ -148,21 +148,38 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects requestMock.AssertSuccess(nameof(service.HandleAddSqlObjectScriptRequest)); Assert.AreEqual(1, service.Projects[projectUri].SqlObjectScripts.Count, "SqlObjectScripts count after re-add"); + // Validate moving a SQL object script + string movedScriptRelativePath = @"SubPath\MyRenamedTable.sql"; + string movedScriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), movedScriptRelativePath); + Directory.CreateDirectory(Path.GetDirectoryName(movedScriptAbsolutePath)!); + + requestMock = new(); + await service.HandleMoveSqlObjectScriptRequest(new MoveItemParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath, + DestinationPath = movedScriptRelativePath + }, requestMock.Object); + + requestMock.AssertSuccess(nameof(service.HandleMoveSqlObjectScriptRequest)); + Assert.IsTrue(File.Exists(movedScriptAbsolutePath), "Script should exist at new location"); + Assert.AreEqual(1, service.Projects[projectUri].SqlObjectScripts.Count, "SqlObjectScripts count after move"); + // Validate deleting a SQL object script requestMock = new(); await service.HandleDeleteSqlObjectScriptRequest(new SqlProjectScriptParams() { ProjectUri = projectUri, - Path = scriptRelativePath + Path = movedScriptRelativePath }, requestMock.Object); requestMock.AssertSuccess(nameof(service.HandleDeleteSqlObjectScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].SqlObjectScripts.Count, "SqlObjectScripts count after delete"); - Assert.IsFalse(File.Exists(scriptFullPath), $"{scriptFullPath} expected to have been deleted from disk"); + Assert.IsFalse(File.Exists(movedScriptAbsolutePath), $"{movedScriptAbsolutePath} expected to have been deleted from disk"); } [Test] - public async Task TestPreDeploymentScriptAddDeleteExclude() + public async Task TestPreDeploymentScriptAddDeleteExcludeMove() { // Setup SqlProjectsService service = new(); @@ -172,9 +189,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects // Validate adding a pre-deployment script MockRequest requestMock = new(); string scriptRelativePath = "PreDeploymentScript.sql"; - string scriptFullPath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); - await File.WriteAllTextAsync(scriptFullPath, "SELECT 'Deployment starting...'"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to be on disk"); + string scriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); + await File.WriteAllTextAsync(scriptAbsolutePath, "SELECT 'Deployment starting...'"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to be on disk"); await service.HandleAddPreDeploymentScriptRequest(new SqlProjectScriptParams() { @@ -196,7 +213,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects requestMock.AssertSuccess(nameof(service.HandleExcludePreDeploymentScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].PreDeployScripts.Count, "PreDeployScripts count after exclude"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to still exist on disk"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to still exist on disk"); // Re-add to set up for Delete requestMock = new(); @@ -207,23 +224,40 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects }, requestMock.Object); requestMock.AssertSuccess(nameof(service.HandleAddPreDeploymentScriptRequest)); - Assert.AreEqual(1, service.Projects[projectUri].PreDeployScripts .Count, "PreDeployScripts count after re-add"); + Assert.AreEqual(1, service.Projects[projectUri].PreDeployScripts.Count, "PreDeployScripts count after re-add"); + + // Validate moving a pre-deployment object script + string movedScriptRelativePath = @"SubPath\RenamedPreDeploymentScript.sql"; + string movedScriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), movedScriptRelativePath); + Directory.CreateDirectory(Path.GetDirectoryName(movedScriptAbsolutePath)!); + + requestMock = new(); + await service.HandleMovePreDeploymentScriptRequest(new MoveItemParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath, + DestinationPath = movedScriptRelativePath + }, requestMock.Object); + + requestMock.AssertSuccess(nameof(service.HandleMovePreDeploymentScriptRequest)); + Assert.IsTrue(File.Exists(movedScriptAbsolutePath), "Script should exist at new location"); + Assert.AreEqual(1, service.Projects[projectUri].PreDeployScripts.Count, "PreDeployScripts count after move"); // Validate deleting a pre-deployment script requestMock = new(); await service.HandleDeletePreDeploymentScriptRequest(new SqlProjectScriptParams() { ProjectUri = projectUri, - Path = scriptRelativePath + Path = movedScriptRelativePath }, requestMock.Object); requestMock.AssertSuccess(nameof(service.HandleDeletePreDeploymentScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].PreDeployScripts.Count, "PreDeployScripts count after delete"); - Assert.IsFalse(File.Exists(scriptFullPath), $"{scriptFullPath} expected to have been deleted from disk"); + Assert.IsFalse(File.Exists(movedScriptAbsolutePath), $"{movedScriptAbsolutePath} expected to have been deleted from disk"); } [Test] - public async Task TestPostDeploymentScriptAddDeleteExclude() + public async Task TestPostDeploymentScriptAddDeleteExcludeMove() { // Setup SqlProjectsService service = new(); @@ -233,9 +267,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects // Validate adding a Post-deployment script MockRequest requestMock = new(); string scriptRelativePath = "PostDeploymentScript.sql"; - string scriptFullPath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); - await File.WriteAllTextAsync(scriptFullPath, "SELECT 'Deployment finished!'"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to be on disk"); + string scriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), scriptRelativePath); + await File.WriteAllTextAsync(scriptAbsolutePath, "SELECT 'Deployment finished!'"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to be on disk"); await service.HandleAddPostDeploymentScriptRequest(new SqlProjectScriptParams() { @@ -257,7 +291,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects requestMock.AssertSuccess(nameof(service.HandleExcludePostDeploymentScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].PostDeployScripts.Count, "PostDeployScripts count after exclude"); - Assert.IsTrue(File.Exists(scriptFullPath), $"{scriptFullPath} expected to still exist on disk"); + Assert.IsTrue(File.Exists(scriptAbsolutePath), $"{scriptAbsolutePath} expected to still exist on disk"); // Re-add to set up for Delete requestMock = new(); @@ -270,17 +304,34 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects requestMock.AssertSuccess(nameof(service.HandleAddPostDeploymentScriptRequest)); Assert.AreEqual(1, service.Projects[projectUri].PostDeployScripts.Count, "PostDeployScripts count after re-add"); + // Validate moving a post-deployment object script + string movedScriptRelativePath = @"SubPath\RenamedPostDeploymentScript.sql"; + string movedScriptAbsolutePath = Path.Join(Path.GetDirectoryName(projectUri), movedScriptRelativePath); + Directory.CreateDirectory(Path.GetDirectoryName(movedScriptAbsolutePath)!); + + requestMock = new(); + await service.HandleMovePostDeploymentScriptRequest(new MoveItemParams() + { + ProjectUri = projectUri, + Path = scriptRelativePath, + DestinationPath = movedScriptRelativePath + }, requestMock.Object); + + requestMock.AssertSuccess(nameof(service.HandleMovePostDeploymentScriptRequest)); + Assert.IsTrue(File.Exists(movedScriptAbsolutePath), "Script should exist at new location"); + Assert.AreEqual(1, service.Projects[projectUri].PostDeployScripts.Count, "PostDeployScripts count after move"); + // Validate deleting a Post-deployment script requestMock = new(); await service.HandleDeletePostDeploymentScriptRequest(new SqlProjectScriptParams() { ProjectUri = projectUri, - Path = scriptRelativePath + Path = movedScriptRelativePath }, requestMock.Object); requestMock.AssertSuccess(nameof(service.HandleDeletePostDeploymentScriptRequest)); Assert.AreEqual(0, service.Projects[projectUri].PostDeployScripts.Count, "PostDeployScripts count after delete"); - Assert.IsFalse(File.Exists(scriptFullPath), $"{scriptFullPath} expected to have been deleted from disk"); + Assert.IsFalse(File.Exists(movedScriptAbsolutePath), $"{movedScriptAbsolutePath} expected to have been deleted from disk"); } #region Database reference tests