From 788dc310815c95d1bcfb0e24acf027e20fed033a Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Wed, 29 Jul 2020 13:43:38 -0700 Subject: [PATCH] Update DacFx deploy and generate script with options (#998) * update deploy and generate script to accept deployment options * add tests * add test with option set to true --- .../DacFx/Contracts/DeployRequest.cs | 6 + .../Contracts/GenerateDeployScriptRequest.cs | 6 + .../DacFx/DeployOperation.cs | 3 +- .../DacFx/GenerateDeployScriptOperation.cs | 13 +- .../DacFx/DacFxserviceTests.cs | 160 ++++++++++++++++++ 5 files changed, 181 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeployRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeployRequest.cs index 3e684f6a..58d2a54f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeployRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeployRequest.cs @@ -4,6 +4,7 @@ // using System.Collections.Generic; using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts { @@ -21,6 +22,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts /// Gets or sets SQLCMD variables for deployment /// public IDictionary SqlCommandVariableValues { get; set; } + + /// + /// Gets or sets the options for deployment + /// + public DeploymentOptions DeploymentOptions { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs index a6c122ed..367eda80 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs @@ -4,6 +4,7 @@ // using System.Collections.Generic; using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts { @@ -26,6 +27,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts /// Gets or sets SQLCMD variables for script generation /// public IDictionary SqlCommandVariableValues { get; set; } + + /// + /// Gets or sets the options for deployment + /// + public DeploymentOptions DeploymentOptions { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs index b299cf5d..0df1edb6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs @@ -5,6 +5,7 @@ using Microsoft.SqlServer.Dac; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.DacFx @@ -25,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx public override void Execute() { DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath); - DacDeployOptions options = this.GetDefaultDeployOptions(); + DacDeployOptions options = this.Parameters.DeploymentOptions != null ? SchemaCompareUtils.CreateSchemaCompareOptions(this.Parameters.DeploymentOptions) : this.GetDefaultDeployOptions(); if (this.Parameters.SqlCommandVariableValues != null) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs index e8a8c2e5..f723d15b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs @@ -2,15 +2,16 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.SqlServer.Dac; -using Microsoft.SqlTools.ServiceLayer.Connection; -using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; -using Microsoft.SqlTools.ServiceLayer.TaskServices; -using Microsoft.SqlTools.Utility; using System; using Microsoft.Data.SqlClient; using System.Diagnostics; using System.IO; +using Microsoft.SqlServer.Dac; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.DacFx { @@ -35,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx PublishOptions publishOptions = new PublishOptions(); publishOptions.GenerateDeploymentReport = this.Parameters.GenerateDeploymentReport; publishOptions.CancelToken = this.CancellationToken; - publishOptions.DeployOptions = this.GetDefaultDeployOptions(); + publishOptions.DeployOptions = this.Parameters.DeploymentOptions != null ? SchemaCompareUtils.CreateSchemaCompareOptions(this.Parameters.DeploymentOptions) : this.GetDefaultDeployOptions(); if (this.Parameters.SqlCommandVariableValues != null) { diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs index 5a8b7eba..4d609988 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs @@ -8,6 +8,7 @@ using System.IO; using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.SqlServer.Dac; +using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; using Microsoft.SqlTools.ServiceLayer.DacFx; @@ -35,6 +36,8 @@ CREATE TABLE [dbo].[table2] [col1] NCHAR(10) NULL )"; + private const string SourceViewScript = @"CREATE VIEW [dbo].[view1] AS SELECT dbo.table1.* FROM dbo.table1"; + private const string TargetScript = @"CREATE TABLE [dbo].[table2] ( [ID] INT NOT NULL PRIMARY KEY, @@ -537,6 +540,163 @@ RETURN 0 } } + /// + /// Verify that options are set correctly for a deploy request + /// + [Fact] + public async void DeployWithOptions() + { + var result = GetLiveAutoCompleteTestObjects(); + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, query: SourceScript, dbNamePrefix: "DacFxDeployOptionsTestSource"); + sourceDb.RunQuery(SourceViewScript); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, query: TargetScript, dbNamePrefix: "DacFxDeployOptionsTestTarget"); + + try + { + DacFxService service = new DacFxService(); + // First extract a db to have a dacpac to deploy later + string dacpacPath = InitialExtract(service, sourceDb, result); + + // Deploy the created dacpac with options + var deployParams = new DeployParams + { + PackageFilePath = dacpacPath, + DatabaseName = targetDb.DatabaseName, + UpgradeExisting = true, + DeploymentOptions = new DeploymentOptions() + { + DropObjectsNotInSource = false, + ExcludeObjectTypes = new[] { ObjectType.Views } + } + }; + + // expect table3 to not have been dropped and view1 to not have been created + await VerifyDeployWithOptions(deployParams, targetDb, service, result.ConnectionInfo, expectedTableResult: "table3", expectedViewResult: null); + + // Deploy the created dacpac with options + var deployNoOptionsParams = new DeployParams + { + PackageFilePath = dacpacPath, + DatabaseName = targetDb.DatabaseName, + UpgradeExisting = true + }; + + // expect table3 to be dropped and view1 created + await VerifyDeployWithOptions(deployNoOptionsParams, targetDb, service, result.ConnectionInfo, expectedTableResult: null, expectedViewResult: "view1"); + + VerifyAndCleanup(dacpacPath); + } + finally + { + sourceDb.Cleanup(); + if (targetDb != null) + { + targetDb.Cleanup(); + } + } + } + + private async Task VerifyDeployWithOptions(DeployParams deployParams, SqlTestDb targetDb, DacFxService service, ConnectionInfo connInfo, string expectedTableResult, string expectedViewResult) + { + var deployOperation = new DeployOperation(deployParams, connInfo); + service.PerformOperation(deployOperation, TaskExecutionMode.Execute); + + using (SqlConnection conn = new SqlConnection(targetDb.ConnectionString)) + { + try + { + await conn.OpenAsync(); + var deployedResult = (string)ReliableConnectionHelper.ExecuteScalar(conn, $"SELECT TABLE_NAME FROM {targetDb.DatabaseName}.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'table3'; "); + Assert.Equal(expectedTableResult, deployedResult); + + deployedResult = (string)ReliableConnectionHelper.ExecuteScalar(conn, $"SELECT TABLE_NAME FROM {targetDb.DatabaseName}.INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'view1'; "); + Assert.Equal(expectedViewResult, deployedResult); + } + finally + { + conn.Close(); + } + } + } + + // + /// Verify that options are set correctly for a generate script request + /// + [Fact] + public async void GenerateDeployScriptWithOptions() + { + var result = GetLiveAutoCompleteTestObjects(); + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, query: SourceScript, dbNamePrefix: "DacFxDeployOptionsTestSource"); + sourceDb.RunQuery(SourceViewScript); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, query: TargetScript, dbNamePrefix: "DacFxDeployOptionsTestTarget"); + + try + { + DacFxService service = new DacFxService(); + // First extract a db to have a dacpac to deploy later + string dacpacPath = InitialExtract(service, sourceDb, result); + + // generate script to deploy the created dacpac with options + var generateScriptFalseOptionParams = new GenerateDeployScriptParams + { + PackageFilePath = dacpacPath, + DatabaseName = targetDb.DatabaseName, + DeploymentOptions = new DeploymentOptions() + { + DropObjectsNotInSource = false, + ExcludeObjectTypes = new[] { ObjectType.Views } + } + }; + + var generateScriptFalseOptionOperation = new GenerateDeployScriptOperation(generateScriptFalseOptionParams, result.ConnectionInfo); + service.PerformOperation(generateScriptFalseOptionOperation, TaskExecutionMode.Execute); + + Assert.DoesNotContain("table3", generateScriptFalseOptionOperation.Result.DatabaseScript); + Assert.DoesNotContain("CREATE VIEW", generateScriptFalseOptionOperation.Result.DatabaseScript); + + // try to deploy with the option set to true to make sure it works + var generateScriptTrueOptionParams = new GenerateDeployScriptParams + { + PackageFilePath = dacpacPath, + DatabaseName = targetDb.DatabaseName, + DeploymentOptions = new DeploymentOptions() + { + DropObjectsNotInSource = true, + ExcludeObjectTypes = new[] { ObjectType.Views } + } + }; + + var generateScriptTrueOptionOperation = new GenerateDeployScriptOperation(generateScriptTrueOptionParams, result.ConnectionInfo); + service.PerformOperation(generateScriptTrueOptionOperation, TaskExecutionMode.Execute); + + Assert.Contains("DROP TABLE [dbo].[table3]", generateScriptTrueOptionOperation.Result.DatabaseScript); + Assert.DoesNotContain("CREATE VIEW", generateScriptTrueOptionOperation.Result.DatabaseScript); + + // now generate script without options + var generateScriptNoOptionsParams = new GenerateDeployScriptParams + { + PackageFilePath = dacpacPath, + DatabaseName = targetDb.DatabaseName, + }; + + var generateScriptNoOptionsOperation = new GenerateDeployScriptOperation(generateScriptNoOptionsParams, result.ConnectionInfo); + service.PerformOperation(generateScriptNoOptionsOperation, TaskExecutionMode.Execute); + + Assert.Contains("table3", generateScriptNoOptionsOperation.Result.DatabaseScript); + Assert.Contains("CREATE VIEW", generateScriptNoOptionsOperation.Result.DatabaseScript); + + VerifyAndCleanup(dacpacPath); + } + finally + { + sourceDb.Cleanup(); + if (targetDb != null) + { + targetDb.Cleanup(); + } + } + } + // /// Verify that options can get retrieved from publish profile ///